diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 94f1ad45255807..a22213208c216a 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM containerbase/node:14.16.1@sha256:1ae9b66d0c6c36cb9993c0dd939dfb5b09553bc26798b51f3c723e0aaa7c653c +FROM containerbase/node:14.17.0@sha256:39419f23c62d0fec2f18255d2c822d499a0d2a9cd9f6fcce4e1f05d6edce111a # renovate: datasource=npm diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 8724fa3829eceb..211a87a8f12cc8 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,5 +1,5 @@ { - "name": "Node.js 14", + "name": "Renovate", "dockerFile": "Dockerfile", "settings": { "terminal.integrated.shell.linux": "/bin/bash" diff --git a/.eslintrc.js b/.eslintrc.js index c2c970e27d2e4b..78f298a23e44d1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -60,6 +60,9 @@ module.exports = { }, ], + // disallow direct `nock` module usage as it causes memory issues. + 'no-restricted-imports': [2, { paths: ['nock'] }], + // Makes no sense to allow type inference for expression parameters, but require typing the response '@typescript-eslint/explicit-function-return-type': [ 'error', @@ -123,6 +126,8 @@ module.exports = { '@typescript-eslint/unbound-method': 0, 'jest/valid-title': [0, { ignoreTypeOfDescribeName: true }], + 'max-classes-per-file': 0, + 'class-methods-use-this': 0, }, }, { diff --git a/.github/label-actions.yml b/.github/label-actions.yml index 284c9ad0ca306f..2e190e13eaeb97 100644 --- a/.github/label-actions.yml +++ b/.github/label-actions.yml @@ -28,7 +28,6 @@ The Renovate team will take a look at the reproduction repository. - Once we confirm the provided repository reproduces the problem, the label will be changed to `reproduction:confirmed`. 'logs:problem': comment: > diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 04df248f5066c7..29a6b83fa52bb4 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -20,8 +20,8 @@ I have verified these changes via: - [ ] Code inspection only, or -- [ ] Newly added unit tests, or -- [ ] No new tests but ran on a real repository, or +- [ ] Newly added/modified unit tests, or +- [ ] No unit tests but ran on a real repository, or - [ ] Both unit tests + ran on a real repository diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index b6a22735b76e08..7a9838dc3933f5 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -8,7 +8,7 @@ env: YARN_PACKAGE_CACHE_KEY: v1 YARN_CACHE_FOLDER: .cache/yarn NODE_VERSION: 14 - PYTHON_VERSION: 3.8 + PYTHON_VERSION: 3.9 SKIP_JAVA_TESTS: true jobs: @@ -47,7 +47,7 @@ jobs: - name: Cache Yarn packages id: yarn_cache_packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.6 with: path: ${{ env.YARN_CACHE_FOLDER }} key: ${{ env.YARN_PACKAGE_CACHE_KEY }}-${{ runner.os }}-yarn_cache-${{ hashFiles('**/yarn.lock') }} @@ -63,10 +63,10 @@ jobs: run: yarn install --frozen-lockfile - name: Unit tests - run: yarn jest --logHeapUsage --maxWorkers=2 --ci + run: yarn jest --maxWorkers=2 --ci - name: Codecov - uses: codecov/codecov-action@v1.5.0 + uses: codecov/codecov-action@v1.5.2 if: always() # build after tests to exclude files @@ -102,7 +102,7 @@ jobs: - name: Cache Yarn packages id: yarn_cache_packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.6 with: path: ${{ env.YARN_CACHE_FOLDER }} key: ${{ env.YARN_PACKAGE_CACHE_KEY }}-${{ runner.os }}-yarn_cache-${{ hashFiles('**/yarn.lock') }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e674e067baaa0..8e5f4da42fbd73 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,7 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] node-version: [14] - python-version: [3.8] + python-version: [3.9] java-version: [11] env: @@ -83,7 +83,7 @@ jobs: - name: Cache Yarn packages id: yarn_cache_packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.6 with: path: ${{ env.YARN_CACHE_FOLDER }} key: ${{ env.YARN_PACKAGE_CACHE_KEY }}-${{ runner.os }}-yarn_cache-${{ hashFiles('**/yarn.lock') }} @@ -99,10 +99,10 @@ jobs: run: yarn install --frozen-lockfile - name: Unit tests - run: yarn jest --logHeapUsage --maxWorkers=2 --ci --coverage ${{ env.coverage }} + run: yarn jest --maxWorkers=2 --ci --coverage ${{ env.coverage }} - name: Codecov - uses: codecov/codecov-action@v1.5.0 + uses: codecov/codecov-action@v1.5.2 if: always() && env.coverage == 'true' # build after tests to exclude build files from tests @@ -139,7 +139,7 @@ jobs: - name: Cache Yarn packages id: yarn_cache_packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.6 with: path: ${{ env.YARN_CACHE_FOLDER }} key: ${{ env.YARN_PACKAGE_CACHE_KEY }}-${{ runner.os }}-yarn_cache-${{ hashFiles('**/yarn.lock') }} @@ -204,7 +204,7 @@ jobs: fetch-depth: 0 - name: Cache Yarn packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.6 with: path: ${{ env.YARN_CACHE_FOLDER }} key: ${{ env.YARN_PACKAGE_CACHE_KEY }}-${{ runner.os }}-yarn_cache-${{ hashFiles('**/yarn.lock') }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 03e6566f709937..b63c36df975345 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -19,7 +19,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v1.0.1 # Override language selection by uncommenting this and choosing your languages # with: @@ -27,7 +27,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v1.0.1 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -41,4 +41,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v1.0.1 diff --git a/.github/workflows/release-npm.yml b/.github/workflows/release-npm.yml index 3ab6672cd840e6..cc196d285890ff 100644 --- a/.github/workflows/release-npm.yml +++ b/.github/workflows/release-npm.yml @@ -57,7 +57,7 @@ jobs: - name: Cache Yarn packages id: yarn_cache_packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.6 with: path: ${{ env.YARN_CACHE_FOLDER }} key: ${{ env.YARN_PACKAGE_CACHE_KEY }}-${{ runner.os }}-yarn_cache-${{ hashFiles('**/yarn.lock') }} diff --git a/docs/development/issue-labeling.md b/docs/development/issue-labeling.md index a0911a26b7027d..6df84a302bef77 100644 --- a/docs/development/issue-labeling.md +++ b/docs/development/issue-labeling.md @@ -3,6 +3,17 @@ We try to keep issues well-classified through use of labels. Any repository collaborator can apply labels according to the below guidelines. +The general idea is that we have: + +- manager (`manager:`) +- versioning (`versioning:`) +- datasource (`datasource:`) +- platform (`platform:`) +- core functionality (`core:`) + +The majority of issues should have at least one of those labels. +These labels should also map approximately to our Conventional Commit scopes. + ## Basic knowledge about Renovate You should know about platforms, package managers, datasources and versioning to label issues effectively. @@ -92,6 +103,20 @@ Use [this search](https://github.com/renovatebot/renovate/issues?q=is%3Aissue+is Use these to mark the platform that is affected by this issue. Keep in mind that an issue can be both affecting a platform and a self hosted instance. +### Core + +
+ Core labels + + core:automerge + core:dashboard + core:onboarding + core:schedule + +
+ +The purpose of these labels is to allow browsing of open issues by the most commonly-used functionality, such as automerging or Dependency Dashboard. + ### Manager "manager" is short for "package manager". @@ -141,7 +166,6 @@ Apply these labels when somebody opens a `feature` type issue requesting a new d logs:problem reproduction:needed reproduction:provided - reproduction:confirmed duplicate @@ -159,7 +183,6 @@ Add a label `logs:problem` to indicate that there's a problem with the logs, and Add a label `reproduction:needed` if nobody's reproduced it in a public repo yet and such a reproduction is necessary before further work can be done. Add the label `reproduction:provided` once there is a public reproduction. -A developer will add the `reproduction:confirmed` once they have checked and confirmed the reproduction. Add a label `duplicate` to issues/PRs that are a duplicate of an earlier issue/PR. diff --git a/docs/development/local-development.md b/docs/development/local-development.md index 641cc3f369ad38..2cbb96b97fa39f 100644 --- a/docs/development/local-development.md +++ b/docs/development/local-development.md @@ -14,7 +14,7 @@ You need the following dependencies for local development: - Node.js `>=14.15.4` - Yarn `^1.22.5` - C++ compiler -- Python `^3.8` +- Python `^3.9` - Java between `8` and `12` We support Node.js versions according to the [Node.js release schedule](https://github.com/nodejs/Release#release-schedule). diff --git a/docs/development/new-package-manager-template.md b/docs/development/new-package-manager-template.md index be1455617ce2ac..fb717152c2cac9 100644 --- a/docs/development/new-package-manager-template.md +++ b/docs/development/new-package-manager-template.md @@ -2,7 +2,7 @@ **Did you read our documentation on adding a package manager?** -- [ ] I've read the [adding a package manager](../../docs/development/adding-a-package-manager.md) documentation. +- [ ] I've read the [adding a package manager](/renovatebot/renovate/blob/HEAD/docs/development/adding-a-package-manager.md) documentation. ## Basics diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index cae189ab710c46..e6b372a013fc41 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -1990,12 +1990,22 @@ In the above example, each regex manager will match a single dependency each. If `depName` cannot be captured with a named capture group in `matchString` then it can be defined manually using this field. It will be compiled using Handlebars and the regex `groups` result. +### extractVersionTemplate + +If `extractVersion` cannot be captured with a named capture group in `matchString` then it can be defined manually using this field. +It will be compiled using Handlebars and the regex `groups` result. + ### lookupNameTemplate `lookupName` is used for looking up dependency versions. It will be compiled using Handlebars and the regex `groups` result. It will default to the value of `depName` if left unconfigured/undefined. +### currentValueTemplate + +If the `currentValue` for a dependency is not captured with a named group then it can be defined in config using this field. +It will be compiled using Handlebars and the regex `groups` result. + ### datasourceTemplate If the `datasource` for a dependency is not captured with a named group then it can be defined in config using this field. @@ -2180,6 +2190,12 @@ If this setting is true then you would get one PR for webpack@v2 and one for web If this is set to a non-zero value, _and_ an update contains a release timestamp header, then Renovate will check if the "stability days" have passed. +Note: Renovate will wait for the set amount of `stabilityDays` to pass for each **separate** version. +Renovate does not wait until the package has seen no releases for x `stabilityDays`. +`stabilityDays` is not intended to help with slowing down fast releasing project updates. +If you want to slow down PRs for a specific package, setup a custom schedule for that package. +Read [our selective-scheduling help](https://docs.renovatebot.com/noise-reduction/#selective-scheduling) to learn how to set the schedule. + If the amount of days since the release is less than the set `stabilityDays` a "pending" status check is added to the branch. If enough days have passed then the "pending" status is removed, and a "passing" status check is added. @@ -2196,6 +2212,22 @@ There are a couple of uses for `stabilityDays`: If you combine `stabilityDays=3` and `prCreation="not-pending"` then Renovate will hold back from creating branches until 3 or more days have elapsed since the version was released. It's recommended that you enable `dependencyDashboard=true` so you don't lose visibility of these pending PRs. +#### Prevent holding broken npm packages + +npm packages less than 72 hours (3 days) old can be unpublished, which could result in a service impact if you have already updated to it. +Set `stabilityDays` to 3 for npm packages to prevent relying on a package that can be removed from the registry: + +```json +{ + "packageRules": [ + { + "matchDatasources": ["npm"], + "stabilityDays": 3 + } + ] +} +``` + #### Await X days before Automerging If you have both `automerge` as well as `stabilityDays` enabled, it means that PRs will be created immediately but automerging will be delayed until X days have passed. @@ -2250,7 +2282,7 @@ To opt in to letting Renovate update internal package versions normally, set thi ## updateNotScheduled When schedules are in use, it generally means "no updates". -However there are cases where updates might be desirable - e.g. if you have configured prCreation=not-pending, or you have rebaseStale=true and the base branch is updated so you want Renovate PRs to be rebased. +However there are cases where updates might be desirable - e.g. if you have configured prCreation=not-pending, or you have rebaseWhen=behind-base-branch and the base branch is updated so you want Renovate PRs to be rebased. This defaults to `true`, meaning that Renovate will perform certain "desirable" updates to _existing_ PRs even when outside of schedule. If you wish to disable all updates outside of scheduled hours then configure this field to `false`. diff --git a/docs/usage/docker.md b/docs/usage/docker.md index 38a6cec386e3c0..5146194127eac7 100644 --- a/docs/usage/docker.md +++ b/docs/usage/docker.md @@ -206,6 +206,53 @@ module.exports = { }; ``` +#### Google Container Registry + +Assume you are running GitLab CI in the Google Cloud, and you are storing your Docker images in the Google Container Registry (GCR). + +Access to the GCR uses Bearer token based authentication. +This token can be obtained by running `gcloud auth print-access-token`, which requires the Google Cloud SDK to be installed. + +The token expires after 60 minutes so you cannot store it in a variable for subsequent builds (like you can with `RENOVATE_TOKEN`). + +When running Renovate in this context the Google access token must be retrieved and injected into the `hostRules` configuration just before Renovate is started. + +_This documentation gives **a few hints** on **a possible way** to achieve this end result._ + +The basic approach is that you create a custom image and then run Renovate as one of the stages of your project. +To make this run independent of any user you should use a [`Project Access Token`](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html) (with Scopes: `api`, `read_api` and `write_repository`) for the project and use this as the `RENOVATE_TOKEN` variable for Gitlab CI. +See also the [renovate-runner repository on GitLab](https://gitlab.com/renovate-bot/renovate-runner) where `.gitlab-ci.yml` configuration examples can be found. + +To get access to the token a custom Renovate Docker image is needed that includes the Google Cloud SDK. +The Dockerfile to create such an image can look like this: + +```Dockerfile +FROM renovate/renovate:25.40.1 +# Include the "Docker tip" which you can find here https://cloud.google.com/sdk/docs/install +# under "Installation" for "Debian/Ubuntu" +RUN ... +``` + +For Renovate to access the Google Container Registry (GCR) it needs the current Google Access Token. +The configuration fragment to do that looks something like this: + +```js +hostRules: [ + { + matchHost: 'eu.gcr.io', + token: 'MyReallySecretTokenThatExpiresAfter60Minutes', + }, +]; +``` + +One way to provide the short-lived Google Access Token to Renovate is by generating these settings into a `config.js` file from within the `.gitlab-ci.yml` right before starting Renovate: + +```yaml +script: + - 'echo "module.exports = { hostRules: [ { matchHost: ''eu.gcr.io'', token: ''"$(gcloud auth print-access-token)"'' } ] };" > config.js' + - renovate $RENOVATE_EXTRA_FLAGS +``` + #### ChartMuseum Maybe you're running your own ChartMuseum server to host your private Helm Charts. diff --git a/docs/usage/gitlab-bot-security.md b/docs/usage/gitlab-bot-security.md index f4a166d9e63af6..4f7b26f41118e3 100644 --- a/docs/usage/gitlab-bot-security.md +++ b/docs/usage/gitlab-bot-security.md @@ -74,7 +74,7 @@ Bot services are better if they are provisioned with a "bot identity" so that us ## Recommended migration Until the hosted app can be reactivated, we recommend users migrate to use self-hosted pipelines to run Renovate. -Please see the [renovate-bot/renovate-runner README on GitLab](https://gitlab.com/renovate-bot/renovate-runner/-/blob/master/README.md) for instructions on how to set this up as easily as possible. +Please see the [renovate-bot/renovate-runner README on GitLab](https://gitlab.com/renovate-bot/renovate-runner/-/blob/HEAD/README.md) for instructions on how to set this up as easily as possible. The Renovate team is working to find a feasible design for the app so that we can reactive it securely in future. We welcome any ideas you may have. diff --git a/docs/usage/node.md b/docs/usage/node.md index 61a17b7c21027b..a42c285ed2de8b 100644 --- a/docs/usage/node.md +++ b/docs/usage/node.md @@ -30,18 +30,18 @@ When Renovate processes your project's repository it will look for the files lis ## Configuring Support Policy -Renovate supports a [`supportPolicy`](./configuration-options.md#supportpolicy) option that can be passed the following values and associated versions (current as of April 2021): +Renovate supports a [`supportPolicy`](./configuration-options.md#supportpolicy) option that can be passed the following values and associated versions (current as of June 2021): **Default:** `lts` -| supportPolicy | versions | description | -| ------------- | -------------- | -------------------------------------------------------- | -| all | 12, 14, 15, 16 | All releases that have not passed their end date | -| lts | 12, 14 | All releases classified as LTS, including in maintenance | -| active | 14, 16 | All releases not in maintenance | -| lts_active | 14 | All releases both LTS and active | -| lts_latest | 14 | The latest LTS release | -| current | 16 | The latest release from "all" | +| supportPolicy | versions | description | +| ------------- | ---------- | -------------------------------------------------------- | +| all | 12, 14, 16 | All releases that have not passed their end date | +| lts | 12, 14 | All releases classified as LTS, including in maintenance | +| active | 14, 16 | All releases not in maintenance | +| lts_active | 14 | All releases both LTS and active | +| lts_latest | 14 | The latest LTS release | +| current | 16 | The latest release from "all" | The version numbers associated with each support policy will be updated as new versions of Node.js are released, moved to LTS or maintenance, etc. diff --git a/docs/usage/nuget.md b/docs/usage/nuget.md index 607c084a18ff89..8c7fd09143481d 100644 --- a/docs/usage/nuget.md +++ b/docs/usage/nuget.md @@ -33,8 +33,8 @@ Alternative feeds can be specified either [in a `NuGet.config` file](https://doc "nuget": { "registryUrls": [ "https://api.nuget.org/v3/index.json", - "http://example1.com/nuget/" - "http://example2.com/nuget/v3/index.json" + "https://example1.com/nuget/", + "https://example2.com/nuget/v3/index.json" ] } ``` diff --git a/docs/usage/private-npm-modules.md b/docs/usage/private-npm-modules.md index 3eff4152c16027..013128823a2b85 100644 --- a/docs/usage/private-npm-modules.md +++ b/docs/usage/private-npm-modules.md @@ -69,7 +69,7 @@ module.exports = { // https://www.jfrog.com/confluence/display/JFROG/npm+Registry // Will be passed as `//artifactory.my-company.com/artifactory/api/npm/npm:_auth=` to `.npmrc` hostType: 'npm', - matchHost: 'https://artifactory.my-company.com/artifactory/api/npm/npm', + matchHost: 'https://artifactory.my-company.com/artifactory/api/npm/npm/', token: process.env.ARTIFACTORY_NPM_TOKEN, authType: 'Basic', }, @@ -77,6 +77,8 @@ module.exports = { }; ``` +**NOTE:** Remember to put a trailing slash at the end of your `matchHost` URL. + **NOTE:** Do not use `NPM_TOKEN` as an environment variable. ### Add npmrc string to Renovate config diff --git a/docs/usage/self-hosted-experimental.md b/docs/usage/self-hosted-experimental.md index 2f00716900c3b6..39e8b789a159af 100644 --- a/docs/usage/self-hosted-experimental.md +++ b/docs/usage/self-hosted-experimental.md @@ -35,6 +35,10 @@ If set to any value, Renovate will always paginate requests to GitHub fully, ins If set to "false" (string), Renovate will remove any existing `package-lock.json` before attempting to update it. +## RENOVATE_X_TERRAFORM_LOCK_FILE + +If set to any value, Renovate will update Terraform lock files and allow lockfile maintenance. + ## RENOVATE_USER_AGENT If set to any string, Renovate will use this as the `user-agent` it sends with HTTP requests. diff --git a/docs/usage/semantic-commits.md b/docs/usage/semantic-commits.md index 6d0dbfbbb020ed..82e4bac80d84dd 100644 --- a/docs/usage/semantic-commits.md +++ b/docs/usage/semantic-commits.md @@ -15,7 +15,8 @@ When Renovate finds Angular-style commits, Renovate will create commit messages - chore(deps): update eslint to v4.2.0 - fix(deps): update express to v4.16.2 -Renovate uses `chore` by default, but uses `fix` for updates to your production dependencies in your `package.json` file. +Renovate uses the `chore` prefix by default. +When you extend `config:base`, Renovate still defaults to `chore`, but will use the `fix` prefix for npm production dependencies (`devDependencies` still use `chore`). ## Manually enabling or disabling semantic commits diff --git a/lib/config-validator.ts b/lib/config-validator.ts index 78350a90ee7c75..bd659398db5ca9 100644 --- a/lib/config-validator.ts +++ b/lib/config-validator.ts @@ -4,12 +4,12 @@ import { dequal } from 'dequal'; import { readFileSync } from 'fs-extra'; import JSON5 from 'json5'; import { configFileNames } from './config/app-strings'; -import { getConfig as getFileConfig } from './config/file'; import { massageConfig } from './config/massage'; import { migrateConfig } from './config/migration'; import type { RenovateConfig } from './config/types'; import { validateConfig } from './config/validation'; import { logger } from './logger'; +import { getConfig as getFileConfig } from './workers/global/config/parse/file'; /* eslint-disable no-console */ diff --git a/lib/config/keys/__fixtures__/private.pem b/lib/config/__fixtures__/private.pem similarity index 100% rename from lib/config/keys/__fixtures__/private.pem rename to lib/config/__fixtures__/private.pem diff --git a/lib/config/admin.ts b/lib/config/admin.ts index 73f289d2094761..83eb5bdfcae106 100644 --- a/lib/config/admin.ts +++ b/lib/config/admin.ts @@ -8,6 +8,7 @@ const repoAdminOptions = [ 'allowPostUpgradeCommandTemplating', 'allowScripts', 'allowedPostUpgradeCommands', + 'binarySource', 'customEnvVariables', 'dockerChildPrefix', 'dockerImagePrefix', diff --git a/lib/config/decrypt.spec.ts b/lib/config/decrypt.spec.ts index 3dfc0b95ad7327..427ca926a411a2 100644 --- a/lib/config/decrypt.spec.ts +++ b/lib/config/decrypt.spec.ts @@ -3,7 +3,7 @@ import { setAdminConfig } from './admin'; import { decryptConfig } from './decrypt'; import type { RenovateConfig } from './types'; -const privateKey = loadFixture('private.pem', 'keys'); +const privateKey = loadFixture('private.pem', '.'); describe(getName(), () => { describe('decryptConfig()', () => { diff --git a/lib/config/defaults.ts b/lib/config/defaults.ts index 03df2283a34f85..56bd1a6b4d41ed 100644 --- a/lib/config/defaults.ts +++ b/lib/config/defaults.ts @@ -1,5 +1,5 @@ import { getOptions } from './definitions'; -import type { GlobalConfig, RenovateOptions } from './types'; +import type { AllConfig, RenovateOptions } from './types'; const defaultValues = { boolean: true, @@ -14,9 +14,9 @@ export function getDefault(option: RenovateOptions): any { : option.default; } -export function getConfig(): GlobalConfig { +export function getConfig(): AllConfig { const options = getOptions(); - const config: GlobalConfig = {}; + const config: AllConfig = {}; options.forEach((option) => { if (!option.parent) { config[option.name] = getDefault(option); diff --git a/lib/config/definitions.ts b/lib/config/definitions.ts index 8cff7fab64a1b9..daf81de7c8cca1 100644 --- a/lib/config/definitions.ts +++ b/lib/config/definitions.ts @@ -4,7 +4,6 @@ import { getPlatformList } from '../platform'; import { getVersioningList } from '../versioning'; import * as dockerVersioning from '../versioning/docker'; import * as pep440Versioning from '../versioning/pep440'; -import * as semverVersioning from '../versioning/semver'; import type { RenovateOptions } from './types'; const options: RenovateOptions[] = [ @@ -710,7 +709,6 @@ const options: RenovateOptions[] = [ description: 'Versioning to use for filtering and comparisons.', type: 'string', allowedValues: getVersioningList(), - default: semverVersioning.id, cli: false, env: false, }, @@ -1929,6 +1927,15 @@ const options: RenovateOptions[] = [ cli: false, env: false, }, + { + name: 'currentValueTemplate', + description: + 'Optional currentValue for extracted dependencies. Valid only within a `regexManagers` object.', + type: 'string', + parent: 'regexManagers', + cli: false, + env: false, + }, { name: 'versioningTemplate', description: @@ -1947,6 +1954,15 @@ const options: RenovateOptions[] = [ cli: false, env: false, }, + { + name: 'extractVersionTemplate', + description: + 'Optional extractVersion for extracted dependencies. Valid only within a `regexManagers` object.', + type: 'string', + parent: 'regexManagers', + cli: false, + env: false, + }, { name: 'fetchReleaseNotes', description: 'Allow to disable release notes fetching.', diff --git a/lib/config/index.spec.ts b/lib/config/index.spec.ts index c716f5a4737d92..73744b497e91e2 100644 --- a/lib/config/index.spec.ts +++ b/lib/config/index.spec.ts @@ -1,7 +1,4 @@ -import upath from 'upath'; import { getName } from '../../test/util'; -import { readFile } from '../util/fs'; -import getArgv from './config/__fixtures__/argv'; import { getConfig } from './defaults'; jest.mock('../datasource/npm'); @@ -14,111 +11,6 @@ try { const defaultConfig = getConfig(); describe(getName(), () => { - describe('.parseConfigs(env, defaultArgv)', () => { - let configParser: typeof import('.'); - let defaultArgv: string[]; - let defaultEnv: NodeJS.ProcessEnv; - beforeEach(async () => { - jest.resetModules(); - configParser = await import('./index'); - defaultArgv = getArgv(); - defaultEnv = { RENOVATE_CONFIG_FILE: 'abc' }; - jest.mock('delay', () => Promise.resolve()); - }); - it('supports token in env', async () => { - const env: NodeJS.ProcessEnv = { ...defaultEnv, RENOVATE_TOKEN: 'abc' }; - const parsedConfig = await configParser.parseConfigs(env, defaultArgv); - expect(parsedConfig).toContainEntries([['token', 'abc']]); - }); - - it('supports token in CLI options', async () => { - defaultArgv = defaultArgv.concat([ - '--token=abc', - '--pr-footer=custom', - '--log-context=abc123', - ]); - const parsedConfig = await configParser.parseConfigs( - defaultEnv, - defaultArgv - ); - expect(parsedConfig).toContainEntries([ - ['token', 'abc'], - ['prFooter', 'custom'], - ['logContext', 'abc123'], - ]); - }); - - it('supports forceCli', async () => { - defaultArgv = defaultArgv.concat(['--force-cli=false']); - const env: NodeJS.ProcessEnv = { - ...defaultEnv, - RENOVATE_TOKEN: 'abc', - }; - const parsedConfig = await configParser.parseConfigs(env, defaultArgv); - expect(parsedConfig).toContainEntries([ - ['token', 'abc'], - ['force', null], - ]); - expect(parsedConfig).not.toContainKey('configFile'); - }); - it('supports config.force', async () => { - const configPath = upath.join( - __dirname, - 'config/__fixtures__/with-force.js' - ); - const env: NodeJS.ProcessEnv = { - ...defaultEnv, - RENOVATE_CONFIG_FILE: configPath, - }; - const parsedConfig = await configParser.parseConfigs(env, defaultArgv); - expect(parsedConfig).toContainEntries([ - ['token', 'abcdefg'], - [ - 'force', - { - schedule: null, - }, - ], - ]); - }); - it('reads private key from file', async () => { - const privateKeyPath = upath.join( - __dirname, - 'keys/__fixtures__/private.pem' - ); - const env: NodeJS.ProcessEnv = { - ...defaultEnv, - RENOVATE_PRIVATE_KEY_PATH: privateKeyPath, - }; - const expected = await readFile(privateKeyPath); - const parsedConfig = await configParser.parseConfigs(env, defaultArgv); - - expect(parsedConfig).toContainEntries([['privateKey', expected]]); - }); - it('supports Bitbucket username/passwod', async () => { - defaultArgv = defaultArgv.concat([ - '--platform=bitbucket', - '--username=user', - '--password=pass', - ]); - const parsedConfig = await configParser.parseConfigs( - defaultEnv, - defaultArgv - ); - expect(parsedConfig).toContainEntries([ - ['platform', 'bitbucket'], - ['username', 'user'], - ['password', 'pass'], - ]); - }); - it('massages trailing slash into endpoint', async () => { - defaultArgv = defaultArgv.concat([ - '--endpoint=https://github.renovatebot.com/api/v3', - ]); - const parsed = await configParser.parseConfigs(defaultEnv, defaultArgv); - expect(parsed.endpoint).toEqual('https://github.renovatebot.com/api/v3/'); - }); - }); describe('mergeChildConfig(parentConfig, childConfig)', () => { it('merges', async () => { const parentConfig = { ...defaultConfig }; diff --git a/lib/config/index.ts b/lib/config/index.ts index 366813c25a0c80..9cd401aca6689f 100644 --- a/lib/config/index.ts +++ b/lib/config/index.ts @@ -1,14 +1,8 @@ -import { addStream, logger, setContext } from '../logger'; +import { logger } from '../logger'; import { get, getLanguageList, getManagerList } from '../manager'; -import { ensureDir, getSubDirectory, readFile } from '../util/fs'; -import { ensureTrailingSlash } from '../util/url'; -import * as cliParser from './cli'; -import * as defaultsParser from './defaults'; import * as definitions from './definitions'; -import * as envParser from './env'; -import * as fileParser from './file'; import type { - GlobalConfig, + AllConfig, ManagerConfig, RenovateConfig, RenovateConfigStage, @@ -39,89 +33,10 @@ export function getManagerConfig( return managerConfig; } -export async function parseConfigs( - env: NodeJS.ProcessEnv, - argv: string[] -): Promise { - logger.debug('Parsing configs'); - - // Get configs - const defaultConfig = defaultsParser.getConfig(); - const fileConfig = fileParser.getConfig(env); - const cliConfig = cliParser.getConfig(argv); - const envConfig = envParser.getConfig(env); - - let config: GlobalConfig = mergeChildConfig(fileConfig, envConfig); - config = mergeChildConfig(config, cliConfig); - - const combinedConfig = config; - - config = mergeChildConfig(defaultConfig, config); - - if (config.forceCli) { - const forcedCli = { ...cliConfig }; - delete forcedCli.token; - delete forcedCli.hostRules; - if (config.force) { - config.force = { ...config.force, ...forcedCli }; - } else { - config.force = forcedCli; - } - } - - if (!config.privateKey && config.privateKeyPath) { - config.privateKey = await readFile(config.privateKeyPath); - delete config.privateKeyPath; - } - - if (config.logContext) { - // This only has an effect if logContext was defined via file or CLI, otherwise it would already have been detected in env - setContext(config.logContext); - } - - // Add file logger - // istanbul ignore if - if (config.logFile) { - logger.debug( - `Enabling ${config.logFileLevel} logging to ${config.logFile}` - ); - await ensureDir(getSubDirectory(config.logFile)); - addStream({ - name: 'logfile', - path: config.logFile, - level: config.logFileLevel, - }); - } - - logger.trace({ config: defaultConfig }, 'Default config'); - logger.debug({ config: fileConfig }, 'File config'); - logger.debug({ config: cliConfig }, 'CLI config'); - logger.debug({ config: envConfig }, 'Env config'); - logger.debug({ config: combinedConfig }, 'Combined config'); - - // Get global config - logger.trace({ config }, 'Full config'); - - // Print config - logger.trace({ config }, 'Global config'); - - // Massage endpoint to have a trailing slash - if (config.endpoint) { - logger.debug('Adding trailing slash to endpoint'); - config.endpoint = ensureTrailingSlash(config.endpoint); - } - - // Remove log file entries - delete config.logFile; - delete config.logFileLevel; - - return config; -} - export function filterConfig( - inputConfig: GlobalConfig, + inputConfig: AllConfig, targetStage: RenovateConfigStage -): GlobalConfig { +): AllConfig { logger.trace({ config: inputConfig }, `filterConfig('${targetStage}')`); const outputConfig: RenovateConfig = { ...inputConfig }; const stages = ['global', 'repository', 'package', 'branch', 'pr']; diff --git a/lib/config/presets/__snapshots__/index.spec.ts.snap b/lib/config/presets/__snapshots__/index.spec.ts.snap index fa82ef87f41827..a94385764e81d8 100644 --- a/lib/config/presets/__snapshots__/index.spec.ts.snap +++ b/lib/config/presets/__snapshots__/index.spec.ts.snap @@ -67,9 +67,21 @@ exports[`config/presets/index getPreset handles preset not found 3`] = `undefine exports[`config/presets/index getPreset handles removed presets with a migration 1`] = ` Object { - "dependencyDashboard": true, - "description": Array [ - "Enable Renovate Dependency Dashboard creation", + "extends": Array [ + ":separateMajorReleases", + ":combinePatchMinorReleases", + ":ignoreUnstable", + ":prImmediately", + ":semanticPrefixFixDepsChoreOthers", + ":updateNotScheduled", + ":automergeDisabled", + ":ignoreModulesAndTests", + ":autodetectPinVersions", + ":prHourlyLimit2", + ":prConcurrentLimit20", + "group:monorepos", + "group:recommended", + "workarounds:all", ], } `; @@ -384,6 +396,7 @@ Object { exports[`config/presets/index resolvePreset combines two package alls 1`] = ` Object { "matchPackageNames": Array [ + "@types/eslint", "babel-eslint", ], "matchPackagePrefixes": Array [ @@ -473,6 +486,7 @@ Object { exports[`config/presets/index resolvePreset resolves eslint 1`] = ` Object { "matchPackageNames": Array [ + "@types/eslint", "babel-eslint", ], "matchPackagePrefixes": Array [ @@ -488,6 +502,7 @@ Object { "All lint-related packages", ], "matchPackageNames": Array [ + "@types/eslint", "babel-eslint", "codelyzer", "remark-lint", @@ -516,6 +531,7 @@ Object { "All lint-related packages", ], "matchPackageNames": Array [ + "@types/eslint", "babel-eslint", "codelyzer", "remark-lint", @@ -540,6 +556,7 @@ Object { Object { "groupName": "eslint", "matchPackageNames": Array [ + "@types/eslint", "babel-eslint", ], "matchPackagePrefixes": Array [ diff --git a/lib/config/presets/bitbucket-server/index.spec.ts b/lib/config/presets/bitbucket-server/index.spec.ts index 4aaad95f332645..87bae5b5d7cd2e 100644 --- a/lib/config/presets/bitbucket-server/index.spec.ts +++ b/lib/config/presets/bitbucket-server/index.spec.ts @@ -13,12 +13,9 @@ const basePath = '/rest/api/1.0/projects/some/repos/repo/browse'; describe(getName(), () => { beforeEach(() => { - httpMock.setup(); hostRules.find.mockReturnValue({ token: 'abc' }); }); - afterEach(() => httpMock.reset()); - describe('fetchJSONFile()', () => { it('returns JSON', async () => { httpMock diff --git a/lib/config/presets/bitbucket/index.spec.ts b/lib/config/presets/bitbucket/index.spec.ts index cf26c9410dbfcd..7a69e16f7e6cbe 100644 --- a/lib/config/presets/bitbucket/index.spec.ts +++ b/lib/config/presets/bitbucket/index.spec.ts @@ -14,14 +14,6 @@ describe(getName(), () => { setPlatformApi('bitbucket'); }); - beforeEach(() => { - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); - }); - describe('fetchJSONFile()', () => { it('returns JSON', async () => { const data = { foo: 'bar' }; diff --git a/lib/config/presets/gitea/index.spec.ts b/lib/config/presets/gitea/index.spec.ts index 5582056521e852..1e706c652686f6 100644 --- a/lib/config/presets/gitea/index.spec.ts +++ b/lib/config/presets/gitea/index.spec.ts @@ -14,13 +14,10 @@ const basePath = '/repos/some/repo/contents'; describe(getName(), () => { beforeEach(() => { - httpMock.setup(); hostRules.find.mockReturnValue({ token: 'abc' }); setBaseUrl(giteaApiHost); }); - afterEach(() => httpMock.reset()); - describe('fetchJSONFile()', () => { it('returns JSON', async () => { httpMock diff --git a/lib/config/presets/github/index.spec.ts b/lib/config/presets/github/index.spec.ts index 9e2fffdca405e8..e7b45b1684122d 100644 --- a/lib/config/presets/github/index.spec.ts +++ b/lib/config/presets/github/index.spec.ts @@ -13,12 +13,9 @@ const basePath = '/repos/some/repo/contents'; describe(getName(), () => { beforeEach(() => { - httpMock.setup(); hostRules.find.mockReturnValue({ token: 'abc' }); }); - afterEach(() => httpMock.reset()); - describe('fetchJSONFile()', () => { it('returns JSON', async () => { httpMock diff --git a/lib/config/presets/gitlab/index.spec.ts b/lib/config/presets/gitlab/index.spec.ts index 53d32d88bf09f5..b100dc70f5efcb 100644 --- a/lib/config/presets/gitlab/index.spec.ts +++ b/lib/config/presets/gitlab/index.spec.ts @@ -10,11 +10,6 @@ const basePath = '/api/v4/projects/some%2Frepo/repository'; describe(getName(), () => { beforeEach(() => { jest.resetAllMocks(); - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); }); describe('getPreset()', () => { diff --git a/lib/config/presets/index.spec.ts b/lib/config/presets/index.spec.ts index 763cf30f19335e..912b3d8a89ab96 100644 --- a/lib/config/presets/index.spec.ts +++ b/lib/config/presets/index.spec.ts @@ -188,7 +188,7 @@ describe(getName(), () => { config.extends = ['packages:linters']; const res = await presets.resolveConfigPresets(config); expect(res).toMatchSnapshot(); - expect(res.matchPackageNames).toHaveLength(3); + expect(res.matchPackageNames).toHaveLength(4); expect(res.matchPackagePatterns).toHaveLength(1); expect(res.matchPackagePrefixes).toHaveLength(4); }); @@ -198,7 +198,7 @@ describe(getName(), () => { expect(res).toMatchSnapshot(); const rule = res.packageRules[0]; expect(rule.automerge).toBe(true); - expect(rule.matchPackageNames).toHaveLength(3); + expect(rule.matchPackageNames).toHaveLength(4); expect(rule.matchPackagePatterns).toHaveLength(1); expect(rule.matchPackagePrefixes).toHaveLength(4); }); @@ -394,7 +394,7 @@ describe(getName(), () => { }); describe('getPreset', () => { it('handles removed presets with a migration', async () => { - const res = await presets.getPreset(':masterIssue', {}); + const res = await presets.getPreset(':base', {}); expect(res).toMatchSnapshot(); }); it('handles removed presets with no migration', async () => { diff --git a/lib/config/presets/index.ts b/lib/config/presets/index.ts index edde57d982dbe6..d3c6bde5a36b95 100644 --- a/lib/config/presets/index.ts +++ b/lib/config/presets/index.ts @@ -8,7 +8,7 @@ import { ExternalHostError } from '../../types/errors/external-host-error'; import { regEx } from '../../util/regex'; import * as massage from '../massage'; import * as migration from '../migration'; -import type { GlobalConfig, RenovateConfig } from '../types'; +import type { AllConfig, RenovateConfig } from '../types'; import { mergeChildConfig } from '../utils'; import { removedPresets } from './common'; import * as gitea from './gitea'; @@ -222,11 +222,11 @@ export async function getPreset( } export async function resolveConfigPresets( - inputConfig: GlobalConfig, + inputConfig: AllConfig, baseConfig?: RenovateConfig, ignorePresets?: string[], existingPresets: string[] = [] -): Promise { +): Promise { if (!ignorePresets || ignorePresets.length === 0) { ignorePresets = inputConfig.ignorePresets || []; // eslint-disable-line } @@ -234,7 +234,7 @@ export async function resolveConfigPresets( { config: inputConfig, existingPresets }, 'resolveConfigPresets' ); - let config: GlobalConfig = {}; + let config: AllConfig = {}; // First, merge all the preset configs from left to right if (inputConfig.extends?.length) { for (const preset of inputConfig.extends) { diff --git a/lib/config/presets/internal/monorepo.ts b/lib/config/presets/internal/monorepo.ts index 16b6a696aab30f..5de779de0f1406 100644 --- a/lib/config/presets/internal/monorepo.ts +++ b/lib/config/presets/internal/monorepo.ts @@ -25,6 +25,7 @@ const repoGroups = { 'azure azure-storage-net': 'https://github.com/Azure/azure-storage-net', 'bugsnag-js': 'https://github.com/bugsnag/bugsnag-js', 'chakra-ui': 'https://github.com/chakra-ui/chakra-ui', + 'contentful-rich-text': 'https://github.com/contentful/rich-text', 'date-io': 'https://github.com/dmtrKovalenko/date-io', 'devextreme-reactive': 'https://github.com/DevExpress/devextreme-reactive', 'electron-forge': 'https://github.com/electron-userland/electron-forge', @@ -42,9 +43,11 @@ const repoGroups = { 'reactivestack-cookies': 'https://github.com/reactivestack/cookies', 'reg-suit': 'https://github.com/reg-viz/reg-suit', 'semantic-release': 'https://github.com/semantic-release/', + 'shopify-app-bridge': 'https://github.com/Shopify/app-bridge', 'System.IO.Abstractions': 'https://github.com/System-IO-Abstractions/System.IO.Abstractions/', 'telus-tds': 'https://github.com/telusdigital/tds', + 'theme-ui': 'https://github.com/system-ui/theme-ui', 'typescript-eslint': 'https://github.com/typescript-eslint/typescript-eslint', 'typography-js': 'https://github.com/KyleAMathews/typography.js', 'vue-cli': 'https://github.com/vuejs/vue-cli', @@ -76,6 +79,7 @@ const repoGroups = { feathers: 'https://github.com/feathersjs/feathers', fimbullinter: 'https://github.com/fimbullinter/wotan', flopflip: 'https://github.com/tdeekens/flopflip', + Fontsource: 'https://github.com/fontsource/fontsource', formatjs: 'https://github.com/formatjs/formatjs', framework7: 'https://github.com/framework7io/framework7', gatsby: 'https://github.com/gatsbyjs/gatsby', @@ -121,6 +125,7 @@ const repoGroups = { openfeign: 'https://github.com/OpenFeign/feign', opentelemetry: 'https://github.com/open-telemetry/opentelemetry-js', OpenTelemetryDotnet: 'https://github.com/open-telemetry/opentelemetry-dotnet', + orleans: 'https://github.com/dotnet/orleans', picasso: 'https://github.com/qlik-oss/picasso.js', pnpjs: 'https://github.com/pnp/pnpjs', playwright: 'https://github.com/Microsoft/playwright', diff --git a/lib/config/presets/internal/packages.ts b/lib/config/presets/internal/packages.ts index 7828a52d9fece0..7cc856ed63ab33 100644 --- a/lib/config/presets/internal/packages.ts +++ b/lib/config/presets/internal/packages.ts @@ -24,7 +24,7 @@ export const presets: Record = { }, eslint: { description: 'All eslint packages', - matchPackageNames: ['babel-eslint'], + matchPackageNames: ['@types/eslint', 'babel-eslint'], matchPackagePrefixes: ['@typescript-eslint/', 'eslint'], }, stylelint: { @@ -58,6 +58,7 @@ export const presets: Record = { 'ember-exam', 'ember-mocha', 'ember-qunit', + 'enzyme', 'istanbul', 'mock-fs', 'nock', @@ -65,7 +66,15 @@ export const presets: Record = { 'proxyquire', 'supertest', ], - matchPackagePrefixes: ['chai', 'jest', 'mocha', 'qunit', 'sinon', 'should'], + matchPackagePrefixes: [ + '@testing-library', + 'chai', + 'jest', + 'mocha', + 'qunit', + 'should', + 'sinon', + ], }, unitTest: { description: 'All unit test packages', diff --git a/lib/config/presets/npm/index.spec.ts b/lib/config/presets/npm/index.spec.ts index ea2633e5002be2..1e251a9f6aedce 100644 --- a/lib/config/presets/npm/index.spec.ts +++ b/lib/config/presets/npm/index.spec.ts @@ -1,4 +1,4 @@ -import nock from 'nock'; +import * as httpMock from '../../../../test/http-mock'; import { getName } from '../../../../test/util'; import { setAdminConfig } from '../../admin'; import * as npm from '.'; @@ -10,13 +10,12 @@ describe(getName(), () => { beforeEach(() => { jest.resetAllMocks(); setAdminConfig(); - nock.cleanAll(); }); afterEach(() => { delete process.env.RENOVATE_CACHE_NPM_MINUTES; }); it('should throw if no package', async () => { - nock('https://registry.npmjs.org').get('/nopackage').reply(404); + httpMock.scope('https://registry.npmjs.org').get('/nopackage').reply(404); await expect( npm.getPreset({ packageName: 'nopackage', presetName: 'default' }) ).rejects.toThrow(/dep not found/); @@ -45,7 +44,8 @@ describe(getName(), () => { '0.0.2': '2018-05-07T07:21:53+02:00', }, }; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/norenovateconfig') .reply(200, presetPackage); await expect( @@ -77,7 +77,8 @@ describe(getName(), () => { '0.0.2': '2018-05-07T07:21:53+02:00', }, }; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/presetnamenotfound') .reply(200, presetPackage); await expect( @@ -112,7 +113,8 @@ describe(getName(), () => { '0.0.2': '2018-05-07T07:21:53+02:00', }, }; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/workingpreset') .reply(200, presetPackage); const res = await npm.getPreset({ packageName: 'workingpreset' }); diff --git a/lib/config/secrets.ts b/lib/config/secrets.ts index c07a0fc912cab3..c9eee725a6de0f 100644 --- a/lib/config/secrets.ts +++ b/lib/config/secrets.ts @@ -6,7 +6,7 @@ import { import { logger } from '../logger'; import { regEx } from '../util/regex'; import { add } from '../util/sanitize'; -import { GlobalConfig, RenovateConfig } from './types'; +import { AllConfig, RenovateConfig } from './types'; const secretNamePattern = '[A-Za-z][A-Za-z0-9_]*'; @@ -40,7 +40,7 @@ function validateSecrets(secrets_: unknown): void { } } -export function validateConfigSecrets(config: GlobalConfig): void { +export function validateConfigSecrets(config: AllConfig): void { validateSecrets(config.secrets); if (config.repositories) { for (const repository of config.repositories) { @@ -113,12 +113,15 @@ function replaceSecretsinObject( return config; } -export function applySecretsToConfig(config: RenovateConfig): RenovateConfig { +export function applySecretsToConfig( + config: RenovateConfig, + secrets = config.secrets +): RenovateConfig { // Add all secrets to be sanitized - if (is.plainObject(config.secrets)) { - for (const secret of Object.values(config.secrets)) { + if (is.plainObject(secrets)) { + for (const secret of Object.values(secrets)) { add(String(secret)); } } - return replaceSecretsinObject(config, config.secrets); + return replaceSecretsinObject(config, secrets); } diff --git a/lib/config/types.ts b/lib/config/types.ts index bd56a58505b444..042826e1aeaf99 100644 --- a/lib/config/types.ts +++ b/lib/config/types.ts @@ -90,6 +90,7 @@ export interface RepoAdminConfig { allowPostUpgradeCommandTemplating?: boolean; allowScripts?: boolean; allowedPostUpgradeCommands?: string[]; + binarySource?: 'docker' | 'global'; customEnvVariables?: Record; dockerChildPrefix?: string; dockerImagePrefix?: string; @@ -200,7 +201,7 @@ export interface RenovateConfig secrets?: Record; } -export interface GlobalConfig extends RenovateConfig, GlobalOnlyConfig {} +export interface AllConfig extends RenovateConfig, GlobalOnlyConfig {} export interface AssigneesAndReviewersConfig { assigneesFromCodeOwners?: boolean; @@ -369,10 +370,6 @@ export interface ManagerConfig extends RenovateConfig { manager: string; } -export interface RenovateCliConfig extends Record { - repositories?: string[]; -} - export interface MigratedConfig { isMigrated: boolean; migratedConfig: RenovateConfig; diff --git a/lib/config/validation.spec.ts b/lib/config/validation.spec.ts index eb7eb914b4f317..886fe778380b0b 100644 --- a/lib/config/validation.spec.ts +++ b/lib/config/validation.spec.ts @@ -352,6 +352,7 @@ describe(getName(), () => { depNameTemplate: 'foo', datasourceTemplate: 'bar', registryUrlTemplate: 'foobar', + extractVersionTemplate: '^(?v\\d+\\.\\d+)', }, ], }; diff --git a/lib/config/validation.ts b/lib/config/validation.ts index 9e876c1b9ab0d4..2cf5b2adc4d269 100644 --- a/lib/config/validation.ts +++ b/lib/config/validation.ts @@ -387,6 +387,8 @@ export async function validateConfig( 'datasourceTemplate', 'versioningTemplate', 'registryUrlTemplate', + 'currentValueTemplate', + 'extractVersionTemplate', ]; // TODO: fix types for (const regexManager of val as any[]) { @@ -514,7 +516,9 @@ export async function validateConfig( message: `Invalid \`${currentPath}.${key}.${res}\` configuration: value is not a url`, }); } - } else if (['customEnvVariables', 'migratePresets'].includes(key)) { + } else if ( + ['customEnvVariables', 'migratePresets', 'secrets'].includes(key) + ) { const res = validatePlainObject(val); if (res !== true) { errors.push({ diff --git a/lib/datasource/api.ts b/lib/datasource/api.ts index 02d5ac1242fafd..14eecf92eeba6b 100644 --- a/lib/datasource/api.ts +++ b/lib/datasource/api.ts @@ -4,22 +4,22 @@ import { ClojureDatasource } from './clojure'; import * as crate from './crate'; import { DartDatasource } from './dart'; import * as docker from './docker'; -import * as galaxy from './galaxy'; -import * as galaxyCollection from './galaxy-collection'; +import { GalaxyDatasource } from './galaxy'; +import { GalaxyCollectionDatasource } from './galaxy-collection'; import * as gitRefs from './git-refs'; import * as gitTags from './git-tags'; import * as githubReleases from './github-releases'; import * as githubTags from './github-tags'; import * as gitlabTags from './gitlab-tags'; import * as go from './go'; -import * as gradleVersion from './gradle-version'; -import * as helm from './helm'; +import { GradleVersionDatasource } from './gradle-version'; +import { HelmDatasource } from './helm'; import * as hex from './hex'; import * as jenkinsPlugins from './jenkins-plugins'; import * as maven from './maven'; import * as npm from './npm'; import * as nuget from './nuget'; -import * as orb from './orb'; +import { OrbDatasource } from './orb'; import * as packagist from './packagist'; import * as pod from './pod'; import * as pypi from './pypi'; @@ -29,7 +29,7 @@ import * as rubygems from './rubygems'; import * as sbtPackage from './sbt-package'; import * as sbtPlugin from './sbt-plugin'; import * as terraformModule from './terraform-module'; -import * as terraformProvider from './terraform-provider'; +import { TerraformProviderDatasource } from './terraform-provider'; import type { DatasourceApi } from './types'; const api = new Map(); @@ -41,22 +41,22 @@ api.set('clojure', new ClojureDatasource()); api.set('crate', crate); api.set('dart', new DartDatasource()); api.set('docker', docker); -api.set('galaxy', galaxy); -api.set('galaxy-collection', galaxyCollection); +api.set('galaxy', new GalaxyDatasource()); +api.set('galaxy-collection', new GalaxyCollectionDatasource()); api.set('git-refs', gitRefs); api.set('git-tags', gitTags); api.set('github-releases', githubReleases); api.set('github-tags', githubTags); api.set('gitlab-tags', gitlabTags); api.set('go', go); -api.set('gradle-version', gradleVersion); -api.set('helm', helm); +api.set('gradle-version', new GradleVersionDatasource()); +api.set('helm', new HelmDatasource()); api.set('hex', hex); api.set('jenkins-plugins', jenkinsPlugins); api.set('maven', maven); api.set('npm', npm); api.set('nuget', nuget); -api.set('orb', orb); +api.set('orb', new OrbDatasource()); api.set('packagist', packagist); api.set('pod', pod); api.set('pypi', pypi); @@ -66,4 +66,4 @@ api.set('rubygems', rubygems); api.set('sbt-package', sbtPackage); api.set('sbt-plugin', sbtPlugin); api.set('terraform-module', terraformModule); -api.set('terraform-provider', terraformProvider); +api.set('terraform-provider', new TerraformProviderDatasource()); diff --git a/lib/datasource/bitbucket-tags/index.spec.ts b/lib/datasource/bitbucket-tags/index.spec.ts index ab7b800cf490c5..83290ddd9b5357 100644 --- a/lib/datasource/bitbucket-tags/index.spec.ts +++ b/lib/datasource/bitbucket-tags/index.spec.ts @@ -4,13 +4,6 @@ import { getName } from '../../../test/util'; import { id as datasource } from '.'; describe(getName(), () => { - beforeEach(() => { - httpMock.reset(); - httpMock.setup(); - }); - afterEach(() => { - httpMock.reset(); - }); describe('getReleases', () => { it('returns tags from bitbucket cloud', async () => { const body = { diff --git a/lib/datasource/cdnjs/index.spec.ts b/lib/datasource/cdnjs/index.spec.ts index d81e627fbc4d5c..c7aed56bcd1581 100644 --- a/lib/datasource/cdnjs/index.spec.ts +++ b/lib/datasource/cdnjs/index.spec.ts @@ -16,11 +16,6 @@ describe(getName(), () => { describe('getReleases', () => { beforeEach(() => { jest.clearAllMocks(); - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); }); it('throws for empty result', async () => { diff --git a/lib/datasource/clojure/__snapshots__/index.spec.ts.snap b/lib/datasource/clojure/__snapshots__/index.spec.ts.snap index 20e9cbbd5f7794..81e923cf1bd916 100644 --- a/lib/datasource/clojure/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/clojure/__snapshots__/index.spec.ts.snap @@ -640,40 +640,3 @@ Array [ }, ] `; - -exports[`datasource/clojure/index supports file protocol 1`] = ` -Object { - "display": "org.example:package", - "group": "org.example", - "homepage": "https://package.example.org/about", - "name": "package", - "registryUrl": "file:///bar", - "releases": Array [ - Object { - "version": "1.0.0", - }, - Object { - "version": "1.0.1", - }, - Object { - "version": "1.0.2", - }, - Object { - "version": "2.0.0", - }, - ], -} -`; - -exports[`datasource/clojure/index supports file protocol 2`] = ` -Array [ - Array [ - "/bar/org/example/package/maven-metadata.xml", - "utf8", - ], - Array [ - "/bar/org/example/package/2.0.0/package-2.0.0.pom", - "utf8", - ], -] -`; diff --git a/lib/datasource/clojure/index.spec.ts b/lib/datasource/clojure/index.spec.ts index 5285ec397c6ce0..546bb2e44192b3 100644 --- a/lib/datasource/clojure/index.spec.ts +++ b/lib/datasource/clojure/index.spec.ts @@ -1,15 +1,11 @@ -import _fs from 'fs-extra'; import upath from 'upath'; import { ReleaseResult, getPkgReleases } from '..'; import * as httpMock from '../../../test/http-mock'; -import { getName, loadFixture, mocked } from '../../../test/util'; +import { getName, loadFixture } from '../../../test/util'; import * as hostRules from '../../util/host-rules'; import { id as versioning } from '../../versioning/maven'; import { ClojureDatasource } from '.'; -jest.mock('fs-extra'); -const fs = mocked(_fs); - const baseUrl = 'https://clojars.org/repo'; const baseUrlCustom = 'https://custom.registry.renovatebot.com'; @@ -90,12 +86,10 @@ describe(getName(), () => { token: 'abc123', }); jest.resetAllMocks(); - httpMock.setup(); }); afterEach(() => { hostRules.clear(); - httpMock.reset(); delete process.env.RENOVATE_EXPERIMENTAL_NO_MAVEN_POM_CHECK; }); @@ -243,23 +237,4 @@ describe(getName(), () => { expect(sourceUrl).toEqual('https://github.com/example/test'); }); - - it('supports file protocol', async () => { - fs.exists.mockResolvedValueOnce(false); - - fs.exists.mockResolvedValueOnce(true); - fs.readFile.mockResolvedValueOnce( - Buffer.from(loadFixture('metadata.xml', upath.join('..', 'maven'))) - ); - - fs.exists.mockResolvedValueOnce(true); - fs.readFile.mockResolvedValueOnce( - Buffer.from(loadFixture('pom.xml', upath.join('..', 'maven'))) - ); - - const res = await get('org.example:package', 'file:///foo', 'file:///bar'); - - expect(res).toMatchSnapshot(); - expect(fs.readFile.mock.calls).toMatchSnapshot(); - }); }); diff --git a/lib/datasource/crate/index.spec.ts b/lib/datasource/crate/index.spec.ts index 00ae5986f2c7cb..b62822b12eb36b 100644 --- a/lib/datasource/crate/index.spec.ts +++ b/lib/datasource/crate/index.spec.ts @@ -78,8 +78,6 @@ describe(getName(), () => { let adminConfig: RepoAdminConfig; beforeEach(async () => { - httpMock.setup(); - tmpDir = await dir(); adminConfig = { @@ -96,8 +94,6 @@ describe(getName(), () => { fs.rmdirSync(tmpDir.path, { recursive: true }); tmpDir = null; setAdminConfig(); - - httpMock.reset(); }); it('returns null for missing registry url', async () => { diff --git a/lib/datasource/dart/index.spec.ts b/lib/datasource/dart/index.spec.ts index 0d14f99c40f268..286ac8767dd682 100644 --- a/lib/datasource/dart/index.spec.ts +++ b/lib/datasource/dart/index.spec.ts @@ -8,14 +8,6 @@ const body = loadJsonFixture('shared_preferences.json'); const baseUrl = 'https://pub.dartlang.org/api/packages/'; describe(getName(), () => { - beforeEach(() => { - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); - }); - describe('getReleases', () => { it('returns null for empty result', async () => { httpMock.scope(baseUrl).get('/non_sense').reply(200, null); diff --git a/lib/datasource/datasource.spec.ts b/lib/datasource/datasource.spec.ts index 1df40a4d1e9fb8..6a1255ff2800de 100644 --- a/lib/datasource/datasource.spec.ts +++ b/lib/datasource/datasource.spec.ts @@ -24,14 +24,6 @@ class TestDatasource extends Datasource { } describe(getName(), () => { - beforeEach(() => { - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); - }); - it('should throw on 429', async () => { const testDatasource = new TestDatasource(); diff --git a/lib/datasource/docker/__snapshots__/index.spec.ts.snap b/lib/datasource/docker/__snapshots__/index.spec.ts.snap index be528e281181c5..21c6a14bd9010b 100644 --- a/lib/datasource/docker/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/docker/__snapshots__/index.spec.ts.snap @@ -773,7 +773,26 @@ Array [ exports[`datasource/docker/index getReleases supports labels 1`] = ` Object { "registryUrl": "https://index.docker.io", - "releases": Array [], + "releases": Array [ + Object { + "version": "1.0.0", + }, + Object { + "version": "1.2.3-alpine", + }, + Object { + "version": "1.2.3", + }, + Object { + "version": "1-alpine", + }, + Object { + "version": "2.0.0", + }, + Object { + "version": "2-alpine", + }, + ], "sourceUrl": "https://github.com/renovatebot/renovate", } `; @@ -820,7 +839,7 @@ Array [ "user-agent": "https://github.com/renovatebot/renovate", }, "method": "GET", - "url": "https://registry.company.com/v2/node/manifests/latest", + "url": "https://registry.company.com/v2/node/manifests/2-alpine", }, Object { "headers": Object { @@ -895,7 +914,7 @@ Array [ "user-agent": "https://github.com/renovatebot/renovate", }, "method": "GET", - "url": "https://registry.company.com/v2/node/manifests/latest", + "url": "https://registry.company.com/v2/node/manifests/abc", }, Object { "headers": Object { diff --git a/lib/datasource/docker/common.spec.ts b/lib/datasource/docker/common.spec.ts index 84f07292090f1a..6c652b798a2846 100644 --- a/lib/datasource/docker/common.spec.ts +++ b/lib/datasource/docker/common.spec.ts @@ -10,7 +10,6 @@ jest.mock('../../util/host-rules'); describe(getName(), () => { beforeEach(() => { - httpMock.setup(); hostRules.find.mockReturnValue({ username: 'some-username', password: 'some-password', @@ -20,7 +19,6 @@ describe(getName(), () => { afterEach(() => { jest.resetAllMocks(); - httpMock.reset(); }); describe('getRegistryRepository', () => { @@ -73,4 +71,48 @@ describe(getName(), () => { `); }); }); + describe('getAuthHeaders', () => { + beforeEach(() => { + httpMock + .scope('https://my.local.registry') + .get('/v2/') + .reply(401, '', { 'www-authenticate': 'Authenticate you must' }); + hostRules.hosts.mockReturnValue([]); + }); + + it('returns "authType token" if both provided', async () => { + hostRules.find.mockReturnValue({ + authType: 'some-authType', + token: 'some-token', + }); + + const headers = await dockerCommon.getAuthHeaders( + 'https://my.local.registry', + 'https://my.local.registry/prefix' + ); + + expect(headers).toMatchInlineSnapshot(` + Object { + "authorization": "some-authType some-token", + } + `); + }); + + it('returns "Bearer token" if only token provided', async () => { + hostRules.find.mockReturnValue({ + token: 'some-token', + }); + + const headers = await dockerCommon.getAuthHeaders( + 'https://my.local.registry', + 'https://my.local.registry/prefix' + ); + + expect(headers).toMatchInlineSnapshot(` + Object { + "authorization": "Bearer some-token", + } + `); + }); + }); }); diff --git a/lib/datasource/docker/common.ts b/lib/datasource/docker/common.ts index 14d9d1468e95e7..b90cf21ea0892a 100644 --- a/lib/datasource/docker/common.ts +++ b/lib/datasource/docker/common.ts @@ -81,12 +81,20 @@ export async function getAuthHeaders( 'base64' ); opts.headers = { authorization: `Basic ${auth}` }; + } else if (opts.token) { + const authType = opts.authType ?? 'Bearer'; + logger.trace( + `Using ${authType} token for Docker registry ${registryHost}` + ); + opts.headers = { authorization: `${authType} ${opts.token}` }; + return opts.headers; } delete opts.username; delete opts.password; + delete opts.token; if (authenticateHeader.scheme.toUpperCase() === 'BASIC') { - logger.debug(`Using Basic auth for docker registry ${dockerRepository}`); + logger.trace(`Using Basic auth for docker registry ${registryHost}`); await http.get(apiCheckUrl, opts); return opts.headers; } diff --git a/lib/datasource/docker/index.spec.ts b/lib/datasource/docker/index.spec.ts index 2b82f076385743..7be603c7cb522e 100644 --- a/lib/datasource/docker/index.spec.ts +++ b/lib/datasource/docker/index.spec.ts @@ -44,7 +44,6 @@ function mockEcrAuthReject(msg: string) { describe(getName(), () => { beforeEach(() => { - httpMock.setup(); hostRules.find.mockReturnValue({ username: 'some-username', password: 'some-password', @@ -54,7 +53,6 @@ describe(getName(), () => { afterEach(() => { jest.resetAllMocks(); - httpMock.reset(); }); describe('getDigest', () => { @@ -625,8 +623,18 @@ describe(getName(), () => { .times(3) .reply(200) .get('/node/tags/list?n=10000') - .reply(200, { tags: ['latest'] }) - .get('/node/manifests/latest') + .reply(200, { + tags: [ + '2.0.0', + '2-alpine', + '1-alpine', + '1.0.0', + '1.2.3', + '1.2.3-alpine', + 'abc', + ], + }) + .get('/node/manifests/2-alpine') .reply(200, { schemaVersion: 2, mediaType: MediaType.manifestV2, @@ -657,8 +665,8 @@ describe(getName(), () => { .times(4) .reply(200) .get('/node/tags/list?n=10000') - .reply(200, { tags: ['latest'] }) - .get('/node/manifests/latest') + .reply(200, { tags: ['abc'] }) + .get('/node/manifests/abc') .reply(200, { schemaVersion: 2, mediaType: MediaType.manifestListV2, diff --git a/lib/datasource/docker/index.ts b/lib/datasource/docker/index.ts index b4aec34b47dfb0..843f058ece4802 100644 --- a/lib/datasource/docker/index.ts +++ b/lib/datasource/docker/index.ts @@ -3,7 +3,10 @@ import parseLinkHeader from 'parse-link-header'; import { logger } from '../../logger'; import { ExternalHostError } from '../../types/errors/external-host-error'; import * as packageCache from '../../util/cache/package'; -import * as dockerVersioning from '../../versioning/docker'; +import { + api as dockerVersioning, + id as dockerVersioningId, +} from '../../versioning/docker'; import type { GetReleasesConfig, ReleaseResult } from '../types'; import { defaultRegistryUrls, @@ -24,7 +27,7 @@ import { getTagsQuayRegistry } from './quay'; export { id }; export const customRegistrySupport = true; export { defaultRegistryUrls }; -export const defaultVersioning = dockerVersioning.id; +export const defaultVersioning = dockerVersioningId; export const registryStrategy = 'first'; export const defaultConfig = { @@ -142,6 +145,14 @@ async function getTags( } } +function findLatestStable(tags: string[]): string { + const versions = tags + .filter((v) => dockerVersioning.isValid(v) && dockerVersioning.isStable(v)) + .sort((a, b) => dockerVersioning.sortVersions(a, b)); + + return versions.pop() ?? tags.slice(-1).pop(); +} + /** * docker.getDigest * @@ -228,7 +239,7 @@ export async function getReleases({ releases, }; - const latestTag = tags.includes('latest') ? 'latest' : tags[tags.length - 1]; + const latestTag = tags.includes('latest') ? 'latest' : findLatestStable(tags); const labels = await getLabels(registryHost, dockerRepository, latestTag); if (labels && 'org.opencontainers.image.source' in labels) { ret.sourceUrl = labels['org.opencontainers.image.source']; diff --git a/lib/datasource/docker/types.ts b/lib/datasource/docker/types.ts index a5a2834a89daac..72f45fd37ec7fc 100644 --- a/lib/datasource/docker/types.ts +++ b/lib/datasource/docker/types.ts @@ -3,7 +3,7 @@ * https://docs.docker.com/registry/spec/manifest-v2-2/#media-types */ export enum MediaType { - manifestV1 = 'pplication/vnd.docker.distribution.manifest.v1+json', + manifestV1 = 'application/vnd.docker.distribution.manifest.v1+json', manifestV2 = 'application/vnd.docker.distribution.manifest.v2+json', manifestListV2 = 'application/vnd.docker.distribution.manifest.list.v2+json', } diff --git a/lib/datasource/galaxy-collection/__snapshots__/index.spec.ts.snap b/lib/datasource/galaxy-collection/__snapshots__/index.spec.ts.snap index cc3a0aac71f2f4..79bc53194c0673 100644 --- a/lib/datasource/galaxy-collection/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/galaxy-collection/__snapshots__/index.spec.ts.snap @@ -113,23 +113,6 @@ exports[`datasource/galaxy-collection/index getReleases returns null for empty l exports[`datasource/galaxy-collection/index getReleases returns null for null lookupName 1`] = `Array []`; -exports[`datasource/galaxy-collection/index getReleases returns null for remote host error 1`] = `[Error: external-host-error]`; - -exports[`datasource/galaxy-collection/index getReleases returns null for remote host error 2`] = ` -Array [ - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "host": "galaxy.ansible.com", - "user-agent": "https://github.com/renovatebot/renovate", - }, - "method": "GET", - "url": "https://galaxy.ansible.com/api/v2/collections/foo/bar/", - }, -] -`; - exports[`datasource/galaxy-collection/index getReleases returns null for unexpected data at base 1`] = ` Array [ Object { @@ -261,3 +244,43 @@ Array [ }, ] `; + +exports[`datasource/galaxy-collection/index getReleases throws error for remote host versions error 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "galaxy.ansible.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://galaxy.ansible.com/api/v2/collections/community/kubernetes/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "galaxy.ansible.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://galaxy.ansible.com/api/v2/collections/community/kubernetes/versions/", + }, +] +`; + +exports[`datasource/galaxy-collection/index getReleases throws for remote host error 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "galaxy.ansible.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://galaxy.ansible.com/api/v2/collections/foo/bar/", + }, +] +`; diff --git a/lib/datasource/galaxy-collection/index.spec.ts b/lib/datasource/galaxy-collection/index.spec.ts index 8a5725cb1dbd80..9f1420a7cee9c1 100644 --- a/lib/datasource/galaxy-collection/index.spec.ts +++ b/lib/datasource/galaxy-collection/index.spec.ts @@ -1,8 +1,8 @@ import { getPkgReleases } from '..'; import * as httpMock from '../../../test/http-mock'; import { getName, loadFixture } from '../../../test/util'; - -import { id as datasource } from '.'; +import { EXTERNAL_HOST_ERROR } from '../../constants/error-messages'; +import { GalaxyCollectionDatasource } from '.'; const communityKubernetesBase = loadFixture('community_kubernetes_base.json'); const communityKubernetesVersions = loadFixture( @@ -20,34 +20,29 @@ const communityKubernetesDetails0111 = loadFixture( const baseUrl = 'https://galaxy.ansible.com'; +const datasource = GalaxyCollectionDatasource.id; + describe(getName(), () => { describe('getReleases', () => { - beforeEach(() => { - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); - }); - it('returns null for 404 result', async () => { httpMock.scope(baseUrl).get('/api/v2/collections/foo/bar/').reply(404); expect( - await getPkgReleases({ datasource, depName: 'foo.bar' }) + await getPkgReleases({ + datasource, + depName: 'foo.bar', + }) ).toBeNull(); expect(httpMock.getTrace()).toMatchSnapshot(); }); - it('returns null for remote host error', async () => { + it('throws for remote host error', async () => { httpMock.scope(baseUrl).get('/api/v2/collections/foo/bar/').reply(500); - let e; - try { - await getPkgReleases({ datasource, depName: 'foo.bar' }); - } catch (err) { - e = err; - } - expect(e).toBeDefined(); - expect(e).toMatchSnapshot(); + await expect( + getPkgReleases({ + datasource, + depName: 'foo.bar', + }) + ).rejects.toThrow(EXTERNAL_HOST_ERROR); expect(httpMock.getTrace()).toMatchSnapshot(); }); @@ -57,7 +52,10 @@ describe(getName(), () => { .get('/api/v2/collections/community/kubernetes/') .reply(200, ''); expect( - await getPkgReleases({ datasource, depName: 'community.kubernetes' }) + await getPkgReleases({ + datasource, + depName: 'community.kubernetes', + }) ).toBeNull(); expect(httpMock.getTrace()).toMatchSnapshot(); }); @@ -70,11 +68,30 @@ describe(getName(), () => { .get('/api/v2/collections/community/kubernetes/versions/') .reply(200, ''); expect( - await getPkgReleases({ datasource, depName: 'community.kubernetes' }) + await getPkgReleases({ + datasource, + depName: 'community.kubernetes', + }) ).toBeNull(); expect(httpMock.getTrace()).toMatchSnapshot(); }); + it('throws error for remote host versions error', async () => { + httpMock + .scope(baseUrl) + .get('/api/v2/collections/community/kubernetes/') + .reply(200, communityKubernetesBase) + .get('/api/v2/collections/community/kubernetes/versions/') + .reply(500); + await expect( + getPkgReleases({ + datasource, + depName: 'community.kubernetes', + }) + ).rejects.toThrow(EXTERNAL_HOST_ERROR); + expect(httpMock.getTrace()).toMatchSnapshot(); + }); + it('returns only valid versions if a version detail fails', async () => { httpMock .scope(baseUrl) @@ -101,12 +118,22 @@ describe(getName(), () => { }); it('returns null for empty lookup', async () => { - expect(await getPkgReleases({ datasource, depName: '' })).toBeNull(); + expect( + await getPkgReleases({ + datasource, + depName: '', + }) + ).toBeNull(); expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for null lookupName ', async () => { - expect(await getPkgReleases({ datasource, depName: null })).toBeNull(); + expect( + await getPkgReleases({ + datasource, + depName: null, + }) + ).toBeNull(); expect(httpMock.getTrace()).toMatchSnapshot(); }); @@ -116,7 +143,10 @@ describe(getName(), () => { .get('/api/v2/collections/foo/bar/') .replyWithError('some unknown error'); expect( - await getPkgReleases({ datasource, depName: 'foo.bar' }) + await getPkgReleases({ + datasource, + depName: 'foo.bar', + }) ).toBeNull(); expect(httpMock.getTrace()).toMatchSnapshot(); }); diff --git a/lib/datasource/galaxy-collection/index.ts b/lib/datasource/galaxy-collection/index.ts index df2f85e0ceaa8e..856c85561a08f8 100644 --- a/lib/datasource/galaxy-collection/index.ts +++ b/lib/datasource/galaxy-collection/index.ts @@ -1,8 +1,8 @@ import pMap from 'p-map'; import { logger } from '../../logger'; -import { ExternalHostError } from '../../types/errors/external-host-error'; -import * as packageCache from '../../util/cache/package'; -import { Http } from '../../util/http'; +import { cache } from '../../util/cache/package/decorator'; +import type { HttpResponse } from '../../util/http'; +import { Datasource } from '../datasource'; import type { GetReleasesConfig, Release, ReleaseResult } from '../types'; import type { BaseProjectResult, @@ -10,33 +10,36 @@ import type { VersionsProjectResult, } from './types'; -export const id = 'galaxy-collection'; -export const defaultRegistryUrls = ['https://galaxy.ansible.com/']; -export const customRegistrySupport = false; +export class GalaxyCollectionDatasource extends Datasource { + static readonly id = 'galaxy-collection'; -const http = new Http(id); - -export async function getReleases({ - lookupName, - registryUrl, -}: GetReleasesConfig): Promise { - const cacheNamespace = 'datasource-galaxy-collection'; - const cacheKey = lookupName; - const cachedResult = await packageCache.get( - cacheNamespace, - cacheKey - ); - // istanbul ignore if - if (cachedResult) { - return cachedResult; + constructor() { + super(GalaxyCollectionDatasource.id); } - const [namespace, projectName] = lookupName.split('.'); + readonly customRegistrySupport = false; + + readonly defaultRegistryUrls = ['https://galaxy.ansible.com/']; + + @cache({ + namespace: `datasource-${GalaxyCollectionDatasource.id}`, + key: ({ lookupName }: GetReleasesConfig) => lookupName, + }) + async getReleases({ + lookupName, + registryUrl, + }: GetReleasesConfig): Promise { + const [namespace, projectName] = lookupName.split('.'); - const baseUrl = `${registryUrl}api/v2/collections/${namespace}/${projectName}/`; + const baseUrl = `${registryUrl}api/v2/collections/${namespace}/${projectName}/`; + + let baseUrlResponse: HttpResponse; + try { + baseUrlResponse = await this.http.getJson(baseUrl); + } catch (err) { + this.handleGenericErrors(err); + } - try { - const baseUrlResponse = await http.getJson(baseUrl); if (!baseUrlResponse || !baseUrlResponse.body) { logger.warn( { dependency: lookupName }, @@ -48,9 +51,16 @@ export async function getReleases({ const baseProject = baseUrlResponse.body; const versionsUrl = `${baseUrl}versions/`; - const versionsUrlResponse = await http.getJson( - versionsUrl - ); + + let versionsUrlResponse: HttpResponse; + try { + versionsUrlResponse = await this.http.getJson( + versionsUrl + ); + } catch (err) { + this.handleGenericErrors(err); + } + const versionsProject = versionsUrlResponse.body; const releases: Release[] = versionsProject.results.map((value) => { @@ -66,7 +76,7 @@ export async function getReleases({ const enrichedReleases: Release[] = await pMap( releases, (basicRelease) => - http + this.http .getJson( `${versionsUrl}${basicRelease.version}/` ) @@ -107,17 +117,6 @@ export async function getReleases({ homepage: newestVersionDetails?.metadata.homepage, tags: newestVersionDetails?.metadata.tags, }; - - const cacheMinutes = 30; - await packageCache.set(cacheNamespace, cacheKey, result, cacheMinutes); return result; - } catch (err) { - if ( - err.statusCode === 429 || - (err.statusCode >= 500 && err.statusCode < 600) - ) { - throw new ExternalHostError(err); - } - throw err; } } diff --git a/lib/datasource/galaxy/__snapshots__/index.spec.ts.snap b/lib/datasource/galaxy/__snapshots__/index.spec.ts.snap index eccbcde4bb7fc3..2801cfba09c704 100644 --- a/lib/datasource/galaxy/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/galaxy/__snapshots__/index.spec.ts.snap @@ -26,6 +26,7 @@ exports[`datasource/galaxy/index getReleases processes real data 2`] = ` Array [ Object { "headers": Object { + "accept": "application/json", "accept-encoding": "gzip, deflate, br", "host": "galaxy.ansible.com", "user-agent": "https://github.com/renovatebot/renovate", @@ -40,6 +41,7 @@ exports[`datasource/galaxy/index getReleases return null if searching random use Array [ Object { "headers": Object { + "accept": "application/json", "accept-encoding": "gzip, deflate, br", "host": "galaxy.ansible.com", "user-agent": "https://github.com/renovatebot/renovate", @@ -54,6 +56,7 @@ exports[`datasource/galaxy/index getReleases returns null for 404 1`] = ` Array [ Object { "headers": Object { + "accept": "application/json", "accept-encoding": "gzip, deflate, br", "host": "galaxy.ansible.com", "user-agent": "https://github.com/renovatebot/renovate", @@ -68,6 +71,7 @@ exports[`datasource/galaxy/index getReleases returns null for empty list 1`] = ` Array [ Object { "headers": Object { + "accept": "application/json", "accept-encoding": "gzip, deflate, br", "host": "galaxy.ansible.com", "user-agent": "https://github.com/renovatebot/renovate", @@ -82,6 +86,7 @@ exports[`datasource/galaxy/index getReleases returns null for empty result 1`] = Array [ Object { "headers": Object { + "accept": "application/json", "accept-encoding": "gzip, deflate, br", "host": "galaxy.ansible.com", "user-agent": "https://github.com/renovatebot/renovate", @@ -96,6 +101,7 @@ exports[`datasource/galaxy/index getReleases returns null for missing fields 1`] Array [ Object { "headers": Object { + "accept": "application/json", "accept-encoding": "gzip, deflate, br", "host": "galaxy.ansible.com", "user-agent": "https://github.com/renovatebot/renovate", @@ -110,6 +116,7 @@ exports[`datasource/galaxy/index getReleases returns null for unknown error 1`] Array [ Object { "headers": Object { + "accept": "application/json", "accept-encoding": "gzip, deflate, br", "host": "galaxy.ansible.com", "user-agent": "https://github.com/renovatebot/renovate", @@ -126,6 +133,7 @@ exports[`datasource/galaxy/index getReleases throws for 5xx 2`] = ` Array [ Object { "headers": Object { + "accept": "application/json", "accept-encoding": "gzip, deflate, br", "host": "galaxy.ansible.com", "user-agent": "https://github.com/renovatebot/renovate", @@ -140,6 +148,7 @@ exports[`datasource/galaxy/index getReleases throws for 404 1`] = ` Array [ Object { "headers": Object { + "accept": "application/json", "accept-encoding": "gzip, deflate, br", "host": "galaxy.ansible.com", "user-agent": "https://github.com/renovatebot/renovate", diff --git a/lib/datasource/galaxy/index.spec.ts b/lib/datasource/galaxy/index.spec.ts index c4b16844a45f56..fd499327e029ef 100644 --- a/lib/datasource/galaxy/index.spec.ts +++ b/lib/datasource/galaxy/index.spec.ts @@ -1,8 +1,7 @@ import { getPkgReleases } from '..'; import * as httpMock from '../../../test/http-mock'; import { getName, loadFixture } from '../../../test/util'; - -import { id as datasource } from '.'; +import { GalaxyDatasource } from '.'; const res1 = loadFixture('timezone'); const empty = loadFixture('empty'); @@ -11,21 +10,16 @@ const baseUrl = 'https://galaxy.ansible.com/'; describe(getName(), () => { describe('getReleases', () => { - beforeEach(() => { - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); - }); - it('returns null for empty result', async () => { httpMock .scope(baseUrl) .get('/api/v1/roles/?owner__username=non_existent_crate&name=undefined') .reply(200); expect( - await getPkgReleases({ datasource, depName: 'non_existent_crate' }) + await getPkgReleases({ + datasource: GalaxyDatasource.id, + depName: 'non_existent_crate', + }) ).toBeNull(); expect(httpMock.getTrace()).toMatchSnapshot(); }); @@ -35,7 +29,10 @@ describe(getName(), () => { .get('/api/v1/roles/?owner__username=non_existent_crate&name=undefined') .reply(200, undefined); expect( - await getPkgReleases({ datasource, depName: 'non_existent_crate' }) + await getPkgReleases({ + datasource: GalaxyDatasource.id, + depName: 'non_existent_crate', + }) ).toBeNull(); expect(httpMock.getTrace()).toMatchSnapshot(); }); @@ -45,7 +42,10 @@ describe(getName(), () => { .get('/api/v1/roles/?owner__username=non_existent_crate&name=undefined') .reply(200, '\n'); expect( - await getPkgReleases({ datasource, depName: 'non_existent_crate' }) + await getPkgReleases({ + datasource: GalaxyDatasource.id, + depName: 'non_existent_crate', + }) ).toBeNull(); expect(httpMock.getTrace()).toMatchSnapshot(); }); @@ -55,7 +55,10 @@ describe(getName(), () => { .get('/api/v1/roles/?owner__username=some_crate&name=undefined') .reply(404); expect( - await getPkgReleases({ datasource, depName: 'some_crate' }) + await getPkgReleases({ + datasource: GalaxyDatasource.id, + depName: 'some_crate', + }) ).toBeNull(); expect(httpMock.getTrace()).toMatchSnapshot(); }); @@ -65,7 +68,10 @@ describe(getName(), () => { .get('/api/v1/roles/?owner__username=some_crate&name=undefined') .replyWithError('some unknown error'); expect( - await getPkgReleases({ datasource, depName: 'some_crate' }) + await getPkgReleases({ + datasource: GalaxyDatasource.id, + depName: 'some_crate', + }) ).toBeNull(); expect(httpMock.getTrace()).toMatchSnapshot(); }); @@ -75,7 +81,7 @@ describe(getName(), () => { .get('/api/v1/roles/?owner__username=yatesr&name=timezone') .reply(200, res1); const res = await getPkgReleases({ - datasource, + datasource: GalaxyDatasource.id, depName: 'yatesr.timezone', }); expect(res).toMatchSnapshot(); @@ -88,7 +94,10 @@ describe(getName(), () => { .scope(baseUrl) .get('/api/v1/roles/?owner__username=foo&name=bar') .reply(200, empty); - const res = await getPkgReleases({ datasource, depName: 'foo.bar' }); + const res = await getPkgReleases({ + datasource: GalaxyDatasource.id, + depName: 'foo.bar', + }); expect(res).toBeNull(); expect(httpMock.getTrace()).toMatchSnapshot(); }); @@ -99,7 +108,10 @@ describe(getName(), () => { .reply(502); let e; try { - await getPkgReleases({ datasource, depName: 'some_crate' }); + await getPkgReleases({ + datasource: GalaxyDatasource.id, + depName: 'some_crate', + }); } catch (err) { e = err; } @@ -112,7 +124,10 @@ describe(getName(), () => { .scope(baseUrl) .get('/api/v1/roles/?owner__username=foo&name=bar') .reply(404); - const res = await getPkgReleases({ datasource, depName: 'foo.bar' }); + const res = await getPkgReleases({ + datasource: GalaxyDatasource.id, + depName: 'foo.bar', + }); expect(res).toBeNull(); expect(httpMock.getTrace()).toMatchSnapshot(); }); diff --git a/lib/datasource/galaxy/index.ts b/lib/datasource/galaxy/index.ts index 87e41b9346684b..ad9c03595e7b84 100644 --- a/lib/datasource/galaxy/index.ts +++ b/lib/datasource/galaxy/index.ts @@ -1,44 +1,51 @@ import { logger } from '../../logger'; -import { ExternalHostError } from '../../types/errors/external-host-error'; -import * as packageCache from '../../util/cache/package'; -import { Http } from '../../util/http'; +import { cache } from '../../util/cache/package/decorator'; +import { HttpResponse } from '../../util/http'; +import { Datasource } from '../datasource'; import type { GetReleasesConfig, Release, ReleaseResult } from '../types'; +import type { GalaxyResult } from './types'; -export const id = 'galaxy'; -export const defaultRegistryUrls = ['https://galaxy.ansible.com/']; -export const customRegistrySupport = false; - -const http = new Http(id); - -export async function getReleases({ - lookupName, - registryUrl, -}: GetReleasesConfig): Promise { - const cacheNamespace = 'datasource-galaxy'; - const cacheKey = lookupName; - const cachedResult = await packageCache.get( - cacheNamespace, - cacheKey - ); - // istanbul ignore if - if (cachedResult) { - return cachedResult; +export class GalaxyDatasource extends Datasource { + static readonly id = 'galaxy'; + + constructor() { + super(GalaxyDatasource.id); } - const lookUp = lookupName.split('.'); - const userName = lookUp[0]; - const projectName = lookUp[1]; - - const galaxyAPIUrl = - registryUrl + - 'api/v1/roles/?owner__username=' + - userName + - '&name=' + - projectName; - const galaxyProjectUrl = registryUrl + userName + '/' + projectName; - try { - let res: any = await http.get(galaxyAPIUrl); - if (!res || !res.body) { + readonly customRegistrySupport = false; + + readonly defaultRegistryUrls = ['https://galaxy.ansible.com/']; + + @cache({ + namespace: 'datasource-galaxy', + key: (getReleasesConfig: GetReleasesConfig) => getReleasesConfig.lookupName, + }) + async getReleases({ + lookupName, + registryUrl, + }: GetReleasesConfig): Promise { + const lookUp = lookupName.split('.'); + const userName = lookUp[0]; + const projectName = lookUp[1]; + + const galaxyAPIUrl = + registryUrl + + 'api/v1/roles/?owner__username=' + + userName + + '&name=' + + projectName; + const galaxyProjectUrl = registryUrl + userName + '/' + projectName; + + let raw: HttpResponse = null; + try { + raw = await this.http.getJson(galaxyAPIUrl); + } catch (err) { + this.handleGenericErrors(err); + } + + const body = raw?.body; + + if (!body) { logger.warn( { dependency: lookupName }, `Received invalid data from ${galaxyAPIUrl}` @@ -46,18 +53,15 @@ export async function getReleases({ return null; } - res = res.body; - const response = JSON.parse(res); - // istanbul ignore if - if (response.results.length > 1) { + if (body.results.length > 1) { logger.warn( { dependency: lookupName }, `Received multiple results from ${galaxyAPIUrl}` ); return null; } - if (response.results.length === 0) { + if (body.results.length === 0) { logger.info( { dependency: lookupName }, `Received no results from ${galaxyAPIUrl}` @@ -65,7 +69,7 @@ export async function getReleases({ return null; } - const resultObject = response.results[0]; + const resultObject = body.results[0]; const versions = resultObject.summary_fields.versions; const result: ReleaseResult = { @@ -88,16 +92,7 @@ export async function getReleases({ return release; } ); - const cacheMinutes = 10; - await packageCache.set(cacheNamespace, cacheKey, result, cacheMinutes); + return result; - } catch (err) { - if ( - err.statusCode === 429 || - (err.statusCode >= 500 && err.statusCode < 600) - ) { - throw new ExternalHostError(err); - } - throw err; } } diff --git a/lib/datasource/galaxy/types.ts b/lib/datasource/galaxy/types.ts new file mode 100644 index 00000000000000..5b3dbf9918171e --- /dev/null +++ b/lib/datasource/galaxy/types.ts @@ -0,0 +1,12 @@ +export interface GalaxyResult { + results: { + summary_fields: { + versions: { + name: string; + release_date: string; + }[]; + }; + github_user: string; + github_repo: string; + }[]; +} diff --git a/lib/datasource/github-releases/index.spec.ts b/lib/datasource/github-releases/index.spec.ts index 4bf1e18110c932..35ff90fbc92cd4 100644 --- a/lib/datasource/github-releases/index.spec.ts +++ b/lib/datasource/github-releases/index.spec.ts @@ -29,11 +29,6 @@ describe(getName(), () => { hostRules.find.mockReturnValue({ token: 'some-token', }); - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); }); describe('getReleases', () => { diff --git a/lib/datasource/github-tags/index.spec.ts b/lib/datasource/github-tags/index.spec.ts index cd038f8f6c51bf..26ad50fa7ad22b 100644 --- a/lib/datasource/github-tags/index.spec.ts +++ b/lib/datasource/github-tags/index.spec.ts @@ -12,17 +12,12 @@ const githubEnterpriseApiHost = 'https://git.enterprise.com'; describe(getName(), () => { beforeEach(() => { - httpMock.reset(); - httpMock.setup(); jest.resetAllMocks(); hostRules.hosts = jest.fn(() => []); hostRules.find.mockReturnValue({ token: 'some-token', }); }); - afterEach(() => { - httpMock.reset(); - }); describe('getDigest', () => { const lookupName = 'some/dep'; @@ -110,17 +105,12 @@ describe(getName(), () => { }); describe('getReleases', () => { beforeEach(() => { - httpMock.reset(); - httpMock.setup(); jest.resetAllMocks(); hostRules.hosts = jest.fn(() => []); hostRules.find.mockReturnValue({ token: 'some-token', }); }); - afterEach(() => { - httpMock.reset(); - }); const depName = 'some/dep2'; diff --git a/lib/datasource/gitlab-tags/index.spec.ts b/lib/datasource/gitlab-tags/index.spec.ts index 1b106f37faa734..c451a6a8b8abdb 100644 --- a/lib/datasource/gitlab-tags/index.spec.ts +++ b/lib/datasource/gitlab-tags/index.spec.ts @@ -4,13 +4,6 @@ import { getName } from '../../../test/util'; import { id as datasource } from '.'; describe(getName(), () => { - beforeEach(() => { - httpMock.reset(); - httpMock.setup(); - }); - afterEach(() => { - httpMock.reset(); - }); describe('getReleases', () => { it('returns tags from custom registry', async () => { const body = [ diff --git a/lib/datasource/go/index.spec.ts b/lib/datasource/go/index.spec.ts index 0be03b9737deb2..d5c4ea059a00a6 100644 --- a/lib/datasource/go/index.spec.ts +++ b/lib/datasource/go/index.spec.ts @@ -48,13 +48,11 @@ const resGitHubEnterprise = ` describe(getName(), () => { beforeEach(() => { - httpMock.setup(); hostRules.find.mockReturnValue({}); hostRules.hosts.mockReturnValue([]); }); afterEach(() => { - httpMock.reset(); jest.resetAllMocks(); }); @@ -374,7 +372,6 @@ describe(getName(), () => { const tags = [{ name: 'a/v1.0.0' }, { name: 'b/v2.0.0' }]; for (const pkg of packages) { - httpMock.setup(); httpMock .scope('https://api.github.com/') .get('/repos/x/text/tags?per_page=100') @@ -389,7 +386,7 @@ describe(getName(), () => { const httpCalls = httpMock.getTrace(); expect(httpCalls).toMatchSnapshot(); - httpMock.reset(); + httpMock.clear(); } }); it('returns none if no tags match submodules', async () => { @@ -400,7 +397,6 @@ describe(getName(), () => { const tags = [{ name: 'v1.0.0' }, { name: 'v2.0.0' }]; for (const pkg of packages) { - httpMock.setup(); httpMock .scope('https://api.github.com/') .get('/repos/x/text/tags?per_page=100') @@ -413,7 +409,7 @@ describe(getName(), () => { const httpCalls = httpMock.getTrace(); expect(httpCalls).toMatchSnapshot(); - httpMock.reset(); + httpMock.clear(); } }); it('works for nested modules on github v2+ major upgrades', async () => { diff --git a/lib/datasource/gradle-version/index.spec.ts b/lib/datasource/gradle-version/index.spec.ts index 078303f6e647b1..4da63d73df8b05 100644 --- a/lib/datasource/gradle-version/index.spec.ts +++ b/lib/datasource/gradle-version/index.spec.ts @@ -3,12 +3,14 @@ import * as httpMock from '../../../test/http-mock'; import { getName, loadJsonFixture, partial } from '../../../test/util'; import { ExternalHostError } from '../../types/errors/external-host-error'; import { id as versioning } from '../../versioning/gradle'; -import { id as datasource, getReleases } from '.'; +import { GradleVersionDatasource } from '.'; const allResponse: any = loadJsonFixture('all.json'); let config: GetPkgReleasesConfig; +const datasource = GradleVersionDatasource.id; + describe(getName(), () => { describe('getReleases', () => { beforeEach(() => { @@ -18,11 +20,6 @@ describe(getName(), () => { depName: 'abc', }; jest.clearAllMocks(); - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); }); it('processes real data', async () => { @@ -76,12 +73,14 @@ describe(getName(), () => { httpMock .scope('https://services.gradle.org/') .get('/versions/all') - .reply(404); + .reply(500); - httpMock.scope('http://baz.qux').get('/').reply(404); + httpMock.scope('http://baz.qux').get('/').reply(429); + + const gradleVersionDatasource = new GradleVersionDatasource(); await expect( - getReleases( + gradleVersionDatasource.getReleases( partial({ registryUrl: 'https://services.gradle.org/versions/all', }) @@ -89,12 +88,12 @@ describe(getName(), () => { ).rejects.toThrow(ExternalHostError); await expect( - getReleases( + gradleVersionDatasource.getReleases( partial({ registryUrl: 'http://baz.qux', }) ) - ).rejects.toThrow('Response code 404 (Not Found)'); + ).rejects.toThrow(ExternalHostError); expect(httpMock.getTrace()).toMatchSnapshot(); }); }); diff --git a/lib/datasource/gradle-version/index.ts b/lib/datasource/gradle-version/index.ts index 6638ea55b5f01f..56756a7e092b35 100644 --- a/lib/datasource/gradle-version/index.ts +++ b/lib/datasource/gradle-version/index.ts @@ -1,63 +1,71 @@ -import { ExternalHostError } from '../../types/errors/external-host-error'; -import { Http } from '../../util/http'; -import { HttpError } from '../../util/http/types'; +import { cache } from '../../util/cache/package/decorator'; import { regEx } from '../../util/regex'; import * as gradleVersioning from '../../versioning/gradle'; +import { Datasource } from '../datasource'; import type { GetReleasesConfig, Release, ReleaseResult } from '../types'; import type { GradleRelease } from './types'; -export const id = 'gradle-version'; -export const customRegistrySupport = true; -export const defaultRegistryUrls = ['https://services.gradle.org/versions/all']; -export const defaultVersioning = gradleVersioning.id; -export const registryStrategy = 'merge'; +export class GradleVersionDatasource extends Datasource { + static readonly id = 'gradle-version'; -const http = new Http(id); + constructor() { + super(GradleVersionDatasource.id); + } -const buildTimeRegex = regEx( - '^(\\d\\d\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\+\\d\\d\\d\\d)$' -); + readonly defaultRegistryUrls = ['https://services.gradle.org/versions/all']; -function formatBuildTime(timeStr: string): string | null { - if (!timeStr) { - return null; - } - if (buildTimeRegex.test(timeStr)) { - return timeStr.replace(buildTimeRegex, '$1-$2-$3T$4:$5:$6$7'); - } - return null; -} + readonly defaultVersioning = gradleVersioning.id; + + readonly registryStrategy = 'merge'; + + private static readonly buildTimeRegex = regEx( + '^(\\d\\d\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\+\\d\\d\\d\\d)$' + ); -export async function getReleases({ - registryUrl, -}: GetReleasesConfig): Promise { - let releases: Release[]; - try { - const response = await http.getJson(registryUrl); - releases = response.body - .filter((release) => !release.snapshot && !release.nightly) - .map((release) => ({ - version: release.version, - releaseTimestamp: formatBuildTime(release.buildTime), - ...(release.broken && { isDeprecated: release.broken }), - })); - } catch (err) { - if ( - err instanceof HttpError && - err.response?.url === defaultRegistryUrls[0] - ) { - throw new ExternalHostError(err); + @cache({ + namespace: `datasource-${GradleVersionDatasource.id}`, + key: ({ registryUrl }: GetReleasesConfig) => registryUrl, + }) + async getReleases({ + registryUrl, + }: GetReleasesConfig): Promise { + let releases: Release[]; + try { + const response = await this.http.getJson(registryUrl); + releases = response.body + .filter((release) => !release.snapshot && !release.nightly) + .map((release) => ({ + version: release.version, + releaseTimestamp: GradleVersionDatasource.formatBuildTime( + release.buildTime + ), + ...(release.broken && { isDeprecated: release.broken }), + })); + } catch (err) { + this.handleGenericErrors(err); } - throw err; + + const res: ReleaseResult = { + releases, + homepage: 'https://gradle.org', + sourceUrl: 'https://github.com/gradle/gradle', + }; + if (res.releases.length) { + return res; + } + return null; } - const res: ReleaseResult = { - releases, - homepage: 'https://gradle.org', - sourceUrl: 'https://github.com/gradle/gradle', - }; - if (res.releases.length) { - return res; + private static formatBuildTime(timeStr: string): string | null { + if (!timeStr) { + return null; + } + if (GradleVersionDatasource.buildTimeRegex.test(timeStr)) { + return timeStr.replace( + GradleVersionDatasource.buildTimeRegex, + '$1-$2-$3T$4:$5:$6$7' + ); + } + return null; } - return null; } diff --git a/lib/datasource/helm/index.spec.ts b/lib/datasource/helm/index.spec.ts index ff3a14ed97537f..83ce27950c9394 100644 --- a/lib/datasource/helm/index.spec.ts +++ b/lib/datasource/helm/index.spec.ts @@ -1,7 +1,7 @@ import { getPkgReleases } from '..'; import * as httpMock from '../../../test/http-mock'; import { getName, loadFixture } from '../../../test/util'; -import { id as datasource } from '.'; +import { HelmDatasource } from '.'; // Truncated index.yaml file const indexYaml = loadFixture('index.yaml'); @@ -10,17 +10,12 @@ describe(getName(), () => { describe('getReleases', () => { beforeEach(() => { jest.resetAllMocks(); - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); }); it('returns null if lookupName was not provided', async () => { expect( await getPkgReleases({ - datasource, + datasource: HelmDatasource.id, depName: undefined, registryUrls: ['https://example-repository.com'], }) @@ -29,7 +24,7 @@ describe(getName(), () => { it('returns null if repository was not provided', async () => { expect( await getPkgReleases({ - datasource, + datasource: HelmDatasource.id, depName: 'some_chart', registryUrls: [], }) @@ -42,7 +37,7 @@ describe(getName(), () => { .reply(200, null); expect( await getPkgReleases({ - datasource, + datasource: HelmDatasource.id, depName: 'non_existent_chart', registryUrls: ['https://example-repository.com'], }) @@ -56,7 +51,7 @@ describe(getName(), () => { .reply(200, undefined); expect( await getPkgReleases({ - datasource, + datasource: HelmDatasource.id, depName: 'non_existent_chart', registryUrls: ['https://example-repository.com'], }) @@ -70,7 +65,7 @@ describe(getName(), () => { .reply(404); expect( await getPkgReleases({ - datasource, + datasource: HelmDatasource.id, depName: 'some_chart', registryUrls: ['https://example-repository.com'], }) @@ -85,7 +80,7 @@ describe(getName(), () => { let e; try { await getPkgReleases({ - datasource, + datasource: HelmDatasource.id, depName: 'some_chart', registryUrls: ['https://example-repository.com'], }); @@ -103,7 +98,7 @@ describe(getName(), () => { .replyWithError(''); expect( await getPkgReleases({ - datasource, + datasource: HelmDatasource.id, depName: 'some_chart', registryUrls: ['https://example-repository.com'], }) @@ -116,7 +111,7 @@ describe(getName(), () => { .get('/index.yaml') .reply(200, '# A comment'); const releases = await getPkgReleases({ - datasource, + datasource: HelmDatasource.id, depName: 'non_existent_chart', registryUrls: ['https://example-repository.com'], }); @@ -135,7 +130,7 @@ describe(getName(), () => { .get('/index.yaml') .reply(200, res); const releases = await getPkgReleases({ - datasource, + datasource: HelmDatasource.id, depName: 'non_existent_chart', registryUrls: ['https://example-repository.com'], }); @@ -148,7 +143,7 @@ describe(getName(), () => { .get('/index.yaml') .reply(200, indexYaml); const releases = await getPkgReleases({ - datasource, + datasource: HelmDatasource.id, depName: 'non_existent_chart', registryUrls: ['https://example-repository.com'], }); @@ -161,7 +156,7 @@ describe(getName(), () => { .get('/index.yaml') .reply(200, indexYaml); const releases = await getPkgReleases({ - datasource, + datasource: HelmDatasource.id, depName: 'ambassador', registryUrls: ['https://example-repository.com'], }); @@ -175,7 +170,7 @@ describe(getName(), () => { .get('/subdir/index.yaml') .reply(200, indexYaml); await getPkgReleases({ - datasource, + datasource: HelmDatasource.id, depName: 'ambassador', registryUrls: ['https://example-repository.com/subdir'], }); diff --git a/lib/datasource/helm/index.ts b/lib/datasource/helm/index.ts index 3090fe30131aa6..6cf0e412cbab43 100644 --- a/lib/datasource/helm/index.ts +++ b/lib/datasource/helm/index.ts @@ -1,104 +1,90 @@ import is from '@sindresorhus/is'; import { load } from 'js-yaml'; import { logger } from '../../logger'; -import { ExternalHostError } from '../../types/errors/external-host-error'; -import * as packageCache from '../../util/cache/package'; -import { Http } from '../../util/http'; +import { cache } from '../../util/cache/package/decorator'; import { ensureTrailingSlash } from '../../util/url'; +import { Datasource } from '../datasource'; import type { GetReleasesConfig, ReleaseResult } from '../types'; import type { HelmRepository, RepositoryData } from './types'; -export const id = 'helm'; +export class HelmDatasource extends Datasource { + static readonly id = 'helm'; -const http = new Http(id); + constructor() { + super(HelmDatasource.id); + } -export const customRegistrySupport = true; -export const defaultRegistryUrls = ['https://charts.helm.sh/stable']; -export const registryStrategy = 'first'; + readonly defaultRegistryUrls = ['https://charts.helm.sh/stable']; -export const defaultConfig = { - commitMessageTopic: 'Helm release {{depName}}', - group: { - commitMessageTopic: '{{{groupName}}} Helm releases', - }, -}; + readonly defaultConfig = { + commitMessageTopic: 'Helm release {{depName}}', + group: { + commitMessageTopic: '{{{groupName}}} Helm releases', + }, + }; -export async function getRepositoryData( - repository: string -): Promise { - const cacheNamespace = 'datasource-helm'; - const cacheKey = repository; - const cachedIndex = await packageCache.get( - cacheNamespace, - cacheKey - ); - // istanbul ignore if - if (cachedIndex) { - return cachedIndex; - } - let res: any; - try { - res = await http.get('index.yaml', { - baseUrl: ensureTrailingSlash(repository), - }); - if (!res || !res.body) { - logger.warn(`Received invalid response from ${repository}`); - return null; - } - } catch (err) { - if ( - err.statusCode === 429 || - (err.statusCode >= 500 && err.statusCode < 600) - ) { - throw new ExternalHostError(err); + @cache({ + namespace: `datasource-${HelmDatasource.id}`, + key: (repository: string) => repository, + }) + async getRepositoryData(repository: string): Promise { + let res: any; + try { + res = await this.http.get('index.yaml', { + baseUrl: ensureTrailingSlash(repository), + }); + if (!res || !res.body) { + logger.warn(`Received invalid response from ${repository}`); + return null; + } + } catch (err) { + this.handleGenericErrors(err); } - throw err; - } - try { - const doc = load(res.body, { - json: true, - }) as HelmRepository; - if (!is.plainObject(doc)) { + try { + const doc = load(res.body, { + json: true, + }) as HelmRepository; + if (!is.plainObject(doc)) { + logger.warn(`Failed to parse index.yaml from ${repository}`); + return null; + } + const result: RepositoryData = {}; + for (const [name, releases] of Object.entries(doc.entries)) { + result[name] = { + homepage: releases[0].home, + sourceUrl: releases[0].sources ? releases[0].sources[0] : undefined, + releases: releases.map((release) => ({ + version: release.version, + releaseTimestamp: release.created ? release.created : null, + })), + }; + } + + return result; + } catch (err) { logger.warn(`Failed to parse index.yaml from ${repository}`); + logger.debug(err); return null; } - const result: RepositoryData = {}; - for (const [name, releases] of Object.entries(doc.entries)) { - result[name] = { - homepage: releases[0].home, - sourceUrl: releases[0].sources ? releases[0].sources[0] : undefined, - releases: releases.map((release) => ({ - version: release.version, - releaseTimestamp: release.created ? release.created : null, - })), - }; - } - const cacheMinutes = 20; - await packageCache.set(cacheNamespace, cacheKey, result, cacheMinutes); - return result; - } catch (err) { - logger.warn(`Failed to parse index.yaml from ${repository}`); - logger.debug(err); - return null; } -} -export async function getReleases({ - lookupName, - registryUrl: helmRepository, -}: GetReleasesConfig): Promise { - const repositoryData = await getRepositoryData(helmRepository); - if (!repositoryData) { - logger.debug(`Couldn't get index.yaml file from ${helmRepository}`); - return null; - } - const releases = repositoryData[lookupName]; - if (!releases) { - logger.debug( - { dependency: lookupName }, - `Entry ${lookupName} doesn't exist in index.yaml from ${helmRepository}` - ); - return null; + async getReleases({ + lookupName, + registryUrl: helmRepository, + }: GetReleasesConfig): Promise { + const repositoryData = await this.getRepositoryData(helmRepository); + if (!repositoryData) { + logger.debug(`Couldn't get index.yaml file from ${helmRepository}`); + return null; + } + const releases = repositoryData[lookupName]; + if (!releases) { + logger.debug( + { dependency: lookupName }, + `Entry ${lookupName} doesn't exist in index.yaml from ${helmRepository}` + ); + return null; + } + return releases; } - return releases; } diff --git a/lib/datasource/hex/index.spec.ts b/lib/datasource/hex/index.spec.ts index 739be18d1e017a..d6f453018a816a 100644 --- a/lib/datasource/hex/index.spec.ts +++ b/lib/datasource/hex/index.spec.ts @@ -17,12 +17,10 @@ describe(getName(), () => { beforeEach(() => { hostRules.hosts.mockReturnValue([]); hostRules.find.mockReturnValue({}); - httpMock.setup(); }); afterEach(() => { jest.resetAllMocks(); - httpMock.reset(); }); describe('getReleases', () => { diff --git a/lib/datasource/index.spec.ts b/lib/datasource/index.spec.ts index f244fe575c668b..e16a860a733d76 100644 --- a/lib/datasource/index.spec.ts +++ b/lib/datasource/index.spec.ts @@ -7,7 +7,7 @@ import { ExternalHostError } from '../types/errors/external-host-error'; import { loadModules } from '../util/modules'; import { Datasource } from './datasource'; import * as datasourceDocker from './docker'; -import * as datasourceGalaxy from './galaxy'; +import { GalaxyDatasource } from './galaxy'; import * as datasourceGithubTags from './github-tags'; import * as datasourceMaven from './maven'; import * as datasourceNpm from './npm'; @@ -16,13 +16,11 @@ import type { DatasourceApi } from './types'; import * as datasource from '.'; jest.mock('./docker'); -jest.mock('./galaxy'); jest.mock('./maven'); jest.mock('./npm'); jest.mock('./packagist'); const dockerDatasource = mocked(datasourceDocker); -const galaxyDatasource = mocked(datasourceGalaxy); const mavenDatasource = mocked(datasourceMaven); const npmDatasource = mocked(datasourceNpm); const packagistDatasource = mocked(datasourcePackagist); @@ -151,9 +149,8 @@ describe(getName(), () => { expect(res.sourceUrl).toBeDefined(); }); it('ignores and warns for registryUrls', async () => { - galaxyDatasource.getReleases.mockResolvedValue(null); await datasource.getPkgReleases({ - datasource: datasourceGalaxy.id, + datasource: GalaxyDatasource.id, depName: 'some.dep', registryUrls: ['https://google.com/'], }); diff --git a/lib/datasource/index.ts b/lib/datasource/index.ts index 09d3488deddc68..be184fbb3dd81a 100644 --- a/lib/datasource/index.ts +++ b/lib/datasource/index.ts @@ -188,7 +188,7 @@ function resolveRegistryUrls( if (is.nonEmptyArray(extractedUrls)) { logger.warn( { datasource: datasource.id, registryUrls: extractedUrls }, - 'Custom datasources are not allowed for this datasource and will be ignored' + 'Custom registries are not allowed for this datasource and will be ignored' ); } return defaultRegistryUrls; diff --git a/lib/datasource/jenkins-plugins/index.spec.ts b/lib/datasource/jenkins-plugins/index.spec.ts index 0b7fa723353bad..8a2e7eb9a69c54 100644 --- a/lib/datasource/jenkins-plugins/index.spec.ts +++ b/lib/datasource/jenkins-plugins/index.spec.ts @@ -21,7 +21,6 @@ describe(getName(), () => { beforeEach(() => { resetCache(); - httpMock.setup(); process.env.RENOVATE_SKIP_CACHE = 'true'; jest.resetAllMocks(); }); @@ -30,7 +29,6 @@ describe(getName(), () => { if (!httpMock.allUsed()) { throw new Error('Not all http mocks have been used!'); } - httpMock.reset(); process.env.RENOVATE_SKIP_CACHE = SKIP_CACHE; }); diff --git a/lib/datasource/maven/__snapshots__/index.spec.ts.snap b/lib/datasource/maven/__snapshots__/index.spec.ts.snap index 0ad97a19158aba..f401e28a77d53d 100644 --- a/lib/datasource/maven/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/maven/__snapshots__/index.spec.ts.snap @@ -1170,43 +1170,6 @@ Array [ ] `; -exports[`datasource/maven/index supports file protocol 1`] = ` -Object { - "display": "org.example:package", - "group": "org.example", - "homepage": "https://package.example.org/about", - "name": "package", - "registryUrl": "file:///bar", - "releases": Array [ - Object { - "version": "1.0.0", - }, - Object { - "version": "1.0.1", - }, - Object { - "version": "1.0.2", - }, - Object { - "version": "2.0.0", - }, - ], -} -`; - -exports[`datasource/maven/index supports file protocol 2`] = ` -Array [ - Array [ - "/bar/org/example/package/maven-metadata.xml", - "utf8", - ], - Array [ - "/bar/org/example/package/2.0.0/package-2.0.0.pom", - "utf8", - ], -] -`; - exports[`datasource/maven/index throws EXTERNAL_HOST_ERROR for 50x 1`] = ` Array [ Object { diff --git a/lib/datasource/maven/index.spec.ts b/lib/datasource/maven/index.spec.ts index 6324f9a1e28e42..3c48e4c8a8632f 100644 --- a/lib/datasource/maven/index.spec.ts +++ b/lib/datasource/maven/index.spec.ts @@ -1,15 +1,10 @@ -import _fs from 'fs-extra'; import { ReleaseResult, getPkgReleases } from '..'; import * as httpMock from '../../../test/http-mock'; -import { getName, loadFixture, mocked } from '../../../test/util'; import { EXTERNAL_HOST_ERROR } from '../../constants/error-messages'; import * as hostRules from '../../util/host-rules'; import { id as versioning } from '../../versioning/maven'; import { id as datasource } from '.'; -jest.mock('fs-extra'); -const fs = mocked(_fs); - const baseUrl = 'https://repo.maven.apache.org/maven2'; const baseUrlCustom = 'https://custom.registry.renovatebot.com'; @@ -86,12 +81,10 @@ describe(getName(), () => { token: 'abc123', }); jest.resetAllMocks(); - httpMock.setup(); }); afterEach(() => { hostRules.clear(); - httpMock.reset(); delete process.env.RENOVATE_EXPERIMENTAL_NO_MAVEN_POM_CHECK; }); @@ -312,21 +305,6 @@ describe(getName(), () => { expect(httpMock.getTrace()).toMatchSnapshot(); }); - it('supports file protocol', async () => { - fs.exists.mockResolvedValueOnce(false); - - fs.exists.mockResolvedValueOnce(true); - fs.readFile.mockResolvedValueOnce(Buffer.from(loadFixture('metadata.xml'))); - - fs.exists.mockResolvedValueOnce(true); - fs.readFile.mockResolvedValueOnce(Buffer.from(loadFixture('pom.xml'))); - - const res = await get('org.example:package', 'file:///foo', 'file:///bar'); - - expect(res).toMatchSnapshot(); - expect(fs.readFile.mock.calls).toMatchSnapshot(); - }); - describe('fetching parent info', () => { const parentPackage = { dep: 'org.example:parent', diff --git a/lib/datasource/maven/index.ts b/lib/datasource/maven/index.ts index 8b368ed56bb6bd..20e6aef5764e94 100644 --- a/lib/datasource/maven/index.ts +++ b/lib/datasource/maven/index.ts @@ -95,18 +95,6 @@ function isValidArtifactsInfo( return versions.every((v) => info[v] !== undefined); } -async function getArtifactInfo( - version: string, - artifactUrl: url.URL -): Promise { - const proto = artifactUrl.protocol; - if (proto === 'http:' || proto === 'https:') { - const result = await isHttpResourceExists(artifactUrl); - return [version, result]; - } - return [version, true]; -} - async function filterMissingArtifacts( dependency: MavenDependency, repoUrl: string, @@ -130,8 +118,8 @@ async function filterMissingArtifacts( .filter(([_, artifactUrl]) => Boolean(artifactUrl)) .map( ([version, artifactUrl]) => - (): Promise => - getArtifactInfo(version, artifactUrl) + async (): Promise => + [version, await isHttpResourceExists(artifactUrl)] ); const results = await pAll(queue, { concurrency: 5 }); artifactsInfo = results.reduce( diff --git a/lib/datasource/maven/util.ts b/lib/datasource/maven/util.ts index 2e8c96c8c4144a..18316d73df55b2 100644 --- a/lib/datasource/maven/util.ts +++ b/lib/datasource/maven/util.ts @@ -1,5 +1,4 @@ import url from 'url'; -import fs from 'fs-extra'; import { XmlDocument } from 'xmldoc'; import { HOST_DISABLED } from '../../constants/error-messages'; import { logger } from '../../logger'; @@ -100,14 +99,6 @@ export async function downloadHttpProtocol( } } -async function downloadFileProtocol(pkgUrl: url.URL): Promise { - const pkgPath = pkgUrl.toString().replace('file://', ''); - if (!(await fs.exists(pkgPath))) { - return null; - } - return fs.readFile(pkgPath, 'utf8'); -} - export async function isHttpResourceExists( pkgUrl: url.URL | string, hostType = id @@ -150,9 +141,6 @@ export async function downloadMavenXml( let rawContent: string; let authorization: boolean; switch (pkgUrl.protocol) { - case 'file:': - rawContent = await downloadFileProtocol(pkgUrl); - break; case 'http:': case 'https:': ({ authorization, body: rawContent } = await downloadHttpProtocol( diff --git a/lib/datasource/npm/get.spec.ts b/lib/datasource/npm/get.spec.ts index 0c444d8800bf5f..649498bfef7705 100644 --- a/lib/datasource/npm/get.spec.ts +++ b/lib/datasource/npm/get.spec.ts @@ -15,14 +15,9 @@ describe(getName(), () => { beforeEach(() => { jest.clearAllMocks(); resetMemCache(); - httpMock.setup(); hostRules.clear(); }); - afterEach(() => { - httpMock.reset(); - }); - describe('has bearer auth', () => { const configs = [ `registry=https://test.org\n//test.org/:_authToken=XXX`, diff --git a/lib/datasource/npm/index.spec.ts b/lib/datasource/npm/index.spec.ts index 6e98e2b28d1d90..b81d2b79c13e8b 100644 --- a/lib/datasource/npm/index.spec.ts +++ b/lib/datasource/npm/index.spec.ts @@ -18,7 +18,6 @@ let npmResponse: any; describe(getName(), () => { beforeEach(() => { jest.resetAllMocks(); - httpMock.setup(); setAdminConfig(); hostRules.clear(); resetCache(); @@ -52,7 +51,6 @@ describe(getName(), () => { afterEach(() => { delete process.env.RENOVATE_CACHE_NPM_MINUTES; mockDate.reset(); - httpMock.reset(); }); it('should return null for no versions', async () => { diff --git a/lib/datasource/nuget/index.spec.ts b/lib/datasource/nuget/index.spec.ts index 3ed42025a9a961..61091a9ccc5d06 100644 --- a/lib/datasource/nuget/index.spec.ts +++ b/lib/datasource/nuget/index.spec.ts @@ -134,11 +134,6 @@ describe(getName(), () => { jest.resetAllMocks(); hostRules.hosts.mockReturnValue([]); hostRules.find.mockReturnValue({}); - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); }); it(`can't detect nuget feed version`, async () => { diff --git a/lib/datasource/orb/index.spec.ts b/lib/datasource/orb/index.spec.ts index 590ba37c9a2f9d..4d8feff2660969 100644 --- a/lib/datasource/orb/index.spec.ts +++ b/lib/datasource/orb/index.spec.ts @@ -1,7 +1,7 @@ import { getPkgReleases } from '..'; import * as httpMock from '../../../test/http-mock'; import { getName } from '../../../test/util'; -import { id as datasource } from '.'; +import { OrbDatasource } from '.'; const orbData = { data: { @@ -26,15 +26,12 @@ const orbData = { const baseUrl = 'https://circleci.com'; +const datasource = OrbDatasource.id; + describe(getName(), () => { describe('getReleases', () => { beforeEach(() => { jest.clearAllMocks(); - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); }); it('returns null for empty result', async () => { diff --git a/lib/datasource/orb/index.ts b/lib/datasource/orb/index.ts index 46bd7738570f0e..83098ba392322b 100644 --- a/lib/datasource/orb/index.ts +++ b/lib/datasource/orb/index.ts @@ -1,64 +1,57 @@ import { logger } from '../../logger'; -import * as packageCache from '../../util/cache/package'; -import { Http } from '../../util/http'; +import { cache } from '../../util/cache/package/decorator'; +import { Datasource } from '../datasource'; import type { GetReleasesConfig, ReleaseResult } from '../types'; import type { OrbRelease } from './types'; -export const id = 'orb'; -export const defaultRegistryUrls = ['https://circleci.com/']; -export const customRegistrySupport = false; +export class OrbDatasource extends Datasource { + static readonly id = 'orb'; -const http = new Http(id); - -/** - * orb.getReleases - * - * This function will fetch an orb from CircleCI and return all semver versions. - */ -export async function getReleases({ - lookupName, - registryUrl, -}: GetReleasesConfig): Promise { - logger.debug({ lookupName }, 'orb.getReleases()'); - const cacheNamespace = 'orb'; - const cacheKey = lookupName; - const cachedResult = await packageCache.get( - cacheNamespace, - cacheKey - ); - // istanbul ignore if - if (cachedResult) { - return cachedResult; - } - const url = `${registryUrl}graphql-unstable`; - const body = { - query: `{orb(name:"${lookupName}"){name, homeUrl, versions {version, createdAt}}}`, - variables: {}, - }; - const res: OrbRelease = ( - await http.postJson<{ data: { orb: OrbRelease } }>(url, { - body, - }) - ).body.data.orb; - if (!res) { - logger.debug({ lookupName }, 'Failed to look up orb'); - return null; + constructor() { + super(OrbDatasource.id); } - // Simplify response before caching and returning - const dep: ReleaseResult = { - releases: null, - }; - if (res.homeUrl?.length) { - dep.homepage = res.homeUrl; + + customRegistrySupport = false; + + defaultRegistryUrls = ['https://circleci.com/']; + + @cache({ + namespace: `datasource-${OrbDatasource.id}`, + key: ({ lookupName }: GetReleasesConfig) => lookupName, + }) + async getReleases({ + lookupName, + registryUrl, + }: GetReleasesConfig): Promise { + const url = `${registryUrl}graphql-unstable`; + const body = { + query: `{orb(name:"${lookupName}"){name, homeUrl, versions {version, createdAt}}}`, + variables: {}, + }; + const res: OrbRelease = ( + await this.http.postJson<{ data: { orb: OrbRelease } }>(url, { + body, + }) + ).body.data.orb; + if (!res) { + logger.debug({ lookupName }, 'Failed to look up orb'); + return null; + } + // Simplify response before caching and returning + const dep: ReleaseResult = { + releases: null, + }; + if (res.homeUrl?.length) { + dep.homepage = res.homeUrl; + } + dep.homepage = + dep.homepage || `https://circleci.com/developer/orbs/orb/${lookupName}`; + dep.releases = res.versions.map(({ version, createdAt }) => ({ + version, + releaseTimestamp: createdAt || null, + })); + + logger.trace({ dep }, 'dep'); + return dep; } - dep.homepage = - dep.homepage || `https://circleci.com/developer/orbs/orb/${lookupName}`; - dep.releases = res.versions.map(({ version, createdAt }) => ({ - version, - releaseTimestamp: createdAt || null, - })); - logger.trace({ dep }, 'dep'); - const cacheMinutes = 15; - await packageCache.set(cacheNamespace, cacheKey, dep, cacheMinutes); - return dep; } diff --git a/lib/datasource/packagist/index.spec.ts b/lib/datasource/packagist/index.spec.ts index b0fc54e79ad325..4e7e13b990b7da 100644 --- a/lib/datasource/packagist/index.spec.ts +++ b/lib/datasource/packagist/index.spec.ts @@ -21,7 +21,6 @@ describe(getName(), () => { let config: any; beforeEach(() => { jest.resetAllMocks(); - httpMock.setup(); hostRules.find = jest.fn((input) => input); hostRules.hosts = jest.fn(() => []); config = { @@ -33,10 +32,6 @@ describe(getName(), () => { }; }); - afterEach(() => { - httpMock.reset(); - }); - it('supports custom registries', async () => { config = { registryUrls: ['https://composer.renovatebot.com'], diff --git a/lib/datasource/pod/index.spec.ts b/lib/datasource/pod/index.spec.ts index d5a215b15cad2b..66db34635dfcf5 100644 --- a/lib/datasource/pod/index.spec.ts +++ b/lib/datasource/pod/index.spec.ts @@ -19,11 +19,6 @@ describe(getName(), () => { describe('getReleases', () => { beforeEach(() => { jest.resetAllMocks(); - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); }); it('returns null for invalid inputs', async () => { diff --git a/lib/datasource/pypi/index.spec.ts b/lib/datasource/pypi/index.spec.ts index 880faa3bb05adc..c635b07517a9e8 100644 --- a/lib/datasource/pypi/index.spec.ts +++ b/lib/datasource/pypi/index.spec.ts @@ -22,13 +22,11 @@ describe(getName(), () => { beforeEach(() => { process.env = { ...OLD_ENV }; delete process.env.PIP_INDEX_URL; - httpMock.setup(); jest.resetAllMocks(); }); afterEach(() => { process.env = OLD_ENV; - httpMock.reset(); }); it('returns null for empty result', async () => { diff --git a/lib/datasource/repology/index.spec.ts b/lib/datasource/repology/index.spec.ts index 3e61a3675ed0ea..f984ac7bd8070c 100644 --- a/lib/datasource/repology/index.spec.ts +++ b/lib/datasource/repology/index.spec.ts @@ -54,12 +54,6 @@ const fixtureJdk = loadFixture(`openjdk.json`); describe(getName(), () => { describe('getReleases', () => { - beforeEach(() => { - httpMock.setup(); - }); - - afterEach(() => httpMock.reset()); - it('returns null for empty result', async () => { mockResolverCall('debian_stable', 'nginx', 'binname', { status: 200, diff --git a/lib/datasource/ruby-version/index.spec.ts b/lib/datasource/ruby-version/index.spec.ts index d227fc8bf39363..3efbd83d385eef 100644 --- a/lib/datasource/ruby-version/index.spec.ts +++ b/lib/datasource/ruby-version/index.spec.ts @@ -7,14 +7,6 @@ const rubyReleasesHtml = loadFixture('releases.html'); describe(getName(), () => { describe('getReleases', () => { - beforeEach(() => { - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); - }); - it('parses real data', async () => { httpMock .scope('https://www.ruby-lang.org') diff --git a/lib/datasource/rubygems/index.spec.ts b/lib/datasource/rubygems/index.spec.ts index dce97126fb72f2..83a07492d79242 100644 --- a/lib/datasource/rubygems/index.spec.ts +++ b/lib/datasource/rubygems/index.spec.ts @@ -25,13 +25,11 @@ describe(getName(), () => { beforeEach(() => { resetCache(); - httpMock.setup(); process.env.RENOVATE_SKIP_CACHE = 'true'; jest.resetAllMocks(); }); afterEach(() => { - httpMock.reset(); process.env.RENOVATE_SKIP_CACHE = SKIP_CACHE; }); diff --git a/lib/datasource/sbt-package/index.spec.ts b/lib/datasource/sbt-package/index.spec.ts index b91673e75c027c..93be280383f355 100644 --- a/lib/datasource/sbt-package/index.spec.ts +++ b/lib/datasource/sbt-package/index.spec.ts @@ -1,10 +1,10 @@ -import nock from 'nock'; import { getPkgReleases } from '..'; +import * as httpMock from '../../../test/http-mock'; import { getName, loadFixture } from '../../../test/util'; import * as mavenVersioning from '../../versioning/maven'; import { MAVEN_REPO } from '../maven/common'; import { parseIndexDir } from '../sbt-plugin/util'; -import * as sbtPlugin from '.'; +import * as sbtPackage from '.'; const mavenIndexHtml = loadFixture(`maven-index.html`); const sbtPluginIndex = loadFixture(`sbt-plugins-index.html`); @@ -13,21 +13,27 @@ describe(getName(), () => { it('parses Maven index directory', () => { expect(parseIndexDir(mavenIndexHtml)).toMatchSnapshot(); }); + it('parses sbt index directory', () => { expect(parseIndexDir(sbtPluginIndex)).toMatchSnapshot(); }); describe('getPkgReleases', () => { beforeEach(() => { - nock.disableNetConnect(); - nock('https://failed_repo').get('/maven/org/scalatest/').reply(404, null); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://failed_repo') + .get('/maven/org/scalatest/') + .reply(404, null); + httpMock + .scope('https://repo.maven.apache.org') .get('/maven2/com/example/') .reply(200, 'empty_2.12/\n'); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get('/maven2/com/example/empty/') .reply(200, ''); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get('/maven2/org/scalatest/') .times(3) .reply( @@ -40,22 +46,28 @@ describe(getName(), () => { 'scalatest-flatspec_2.12' + 'scalatest-matchers-core_2.12' ); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get('/maven2/org/scalatest/scalatest/') .reply(200, "1.2.0/"); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get('/maven2/org/scalatest/scalatest_2.12/') .reply(200, "4.5.6/"); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get('/maven2/org/scalatest/scalatest-app_2.12/') .reply(200, "3.2.1/"); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get('/maven2/org/scalatest/scalatest-flatspec_2.12/') .reply(200, "3.2.1/"); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get('/maven2/org/scalatest/scalatest-matchers-core_2.12/') .reply(200, "3.2.1/"); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get( '/maven2/org/scalatest/scalatest-app_2.12/6.5.4/scalatest-app_2.12-6.5.4.pom' ) @@ -68,7 +80,8 @@ describe(getName(), () => { '' + '' ); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get( '/maven2/org/scalatest/scalatest-flatspec_2.12/6.5.4/scalatest-flatspec_2.12-6.5.4.pom' ) @@ -80,7 +93,8 @@ describe(getName(), () => { '' + '' ); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get( '/maven2/org/scalatest/scalatest-matchers-core_2.12/6.5.4/scalatest-matchers-core_2.12-6.5.4.pom' ) @@ -91,10 +105,12 @@ describe(getName(), () => { '' ); - nock('https://dl.bintray.com') + httpMock + .scope('https://dl.bintray.com') .get('/sbt/sbt-plugin-releases/com.github.gseitz/') .reply(200, ''); - nock('https://dl.bintray.com') + httpMock + .scope('https://dl.bintray.com') .get('/sbt/sbt-plugin-releases/org.foundweekends/sbt-bintray/') .reply( 200, @@ -106,7 +122,8 @@ describe(getName(), () => { '\n' + '' ); - nock('https://dl.bintray.com') + httpMock + .scope('https://dl.bintray.com') .get( '/sbt/sbt-plugin-releases/org.foundweekends/sbt-bintray/scala_2.12/' ) @@ -121,7 +138,8 @@ describe(getName(), () => { '\n' + '\n' ); - nock('https://dl.bintray.com') + httpMock + .scope('https://dl.bintray.com') .get( '/sbt/sbt-plugin-releases/org.foundweekends/sbt-bintray/scala_2.12/sbt_1.0/' ) @@ -138,35 +156,36 @@ describe(getName(), () => { ); }); - afterEach(() => { - nock.enableNetConnect(); - }); + // TODO: fix mocks + afterEach(() => httpMock.clear(false)); it('returns null in case of errors', async () => { expect( await getPkgReleases({ versioning: mavenVersioning.id, - datasource: sbtPlugin.id, + datasource: sbtPackage.id, depName: 'org.scalatest:scalatest', registryUrls: ['https://failed_repo/maven'], }) ).toBeNull(); }); + it('returns null if there is no version', async () => { expect( await getPkgReleases({ versioning: mavenVersioning.id, - datasource: sbtPlugin.id, + datasource: sbtPackage.id, depName: 'com.example:empty', registryUrls: [], }) ).toBeNull(); }); + it('fetches releases from Maven', async () => { expect( await getPkgReleases({ versioning: mavenVersioning.id, - datasource: sbtPlugin.id, + datasource: sbtPackage.id, depName: 'org.scalatest:scalatest', registryUrls: ['https://failed_repo/maven', MAVEN_REPO], }) @@ -175,10 +194,13 @@ describe(getName(), () => { registryUrl: 'https://repo.maven.apache.org/maven2', releases: [{ version: '1.2.0' }, { version: '1.2.3' }], }); + }); + + it('fetches releases from Maven 2', async () => { expect( await getPkgReleases({ versioning: mavenVersioning.id, - datasource: sbtPlugin.id, + datasource: sbtPackage.id, depName: 'org.scalatest:scalatest_2.12', registryUrls: [], }) @@ -193,7 +215,7 @@ describe(getName(), () => { expect( await getPkgReleases({ versioning: mavenVersioning.id, - datasource: sbtPlugin.id, + datasource: sbtPackage.id, depName: 'org.scalatest:scalatest-app_2.12', registryUrls: [], }) @@ -207,7 +229,7 @@ describe(getName(), () => { expect( await getPkgReleases({ versioning: mavenVersioning.id, - datasource: sbtPlugin.id, + datasource: sbtPackage.id, depName: 'org.scalatest:scalatest-flatspec_2.12', registryUrls: [], }) @@ -220,7 +242,7 @@ describe(getName(), () => { expect( await getPkgReleases({ versioning: mavenVersioning.id, - datasource: sbtPlugin.id, + datasource: sbtPackage.id, depName: 'org.scalatest:scalatest-matchers-core_2.12', registryUrls: [], }) diff --git a/lib/datasource/sbt-plugin/index.spec.ts b/lib/datasource/sbt-plugin/index.spec.ts index efbc89a82ecd3b..aa57f43830b347 100644 --- a/lib/datasource/sbt-plugin/index.spec.ts +++ b/lib/datasource/sbt-plugin/index.spec.ts @@ -1,5 +1,5 @@ -import nock from 'nock'; import { getPkgReleases } from '..'; +import * as httpMock from '../../../test/http-mock'; import { getName, loadFixture } from '../../../test/util'; import * as mavenVersioning from '../../versioning/maven'; import { MAVEN_REPO } from '../maven/common'; @@ -13,15 +13,19 @@ describe(getName(), () => { it('parses Maven index directory', () => { expect(parseIndexDir(mavenIndexHtml)).toMatchSnapshot(); }); + it('parses sbt index directory', () => { expect(parseIndexDir(sbtPluginIndex)).toMatchSnapshot(); }); describe('getPkgReleases', () => { beforeEach(() => { - nock.disableNetConnect(); - nock('https://failed_repo').get('/maven/org/scalatest/').reply(404, null); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://failed_repo') + .get('/maven/org/scalatest/') + .reply(404, null); + httpMock + .scope('https://repo.maven.apache.org') .get('/maven2/org/scalatest/') .reply( 200, @@ -30,17 +34,21 @@ describe(getName(), () => { "scalatest_2.12/" + "scalatest_2.12/" ); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get('/maven2/org/scalatest/scalatest/') .reply(200, "1.2.0/"); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get('/maven2/org/scalatest/scalatest_2.12/') .reply(200, "4.5.6/"); - nock('https://dl.bintray.com') + httpMock + .scope('https://dl.bintray.com') .get('/sbt/sbt-plugin-releases/com.github.gseitz/') .reply(200, ''); - nock('https://dl.bintray.com') + httpMock + .scope('https://dl.bintray.com') .get('/sbt/sbt-plugin-releases/org.foundweekends/sbt-bintray/') .reply( 200, @@ -52,7 +60,8 @@ describe(getName(), () => { '\n' + '' ); - nock('https://dl.bintray.com') + httpMock + .scope('https://dl.bintray.com') .get( '/sbt/sbt-plugin-releases/org.foundweekends/sbt-bintray/scala_2.12/' ) @@ -67,7 +76,8 @@ describe(getName(), () => { '\n' + '\n' ); - nock('https://dl.bintray.com') + httpMock + .scope('https://dl.bintray.com') .get( '/sbt/sbt-plugin-releases/org.foundweekends/sbt-bintray/scala_2.12/sbt_1.0/' ) @@ -83,7 +93,8 @@ describe(getName(), () => { '\n' ); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get('/maven2/io/get-coursier/') .reply( 200, @@ -92,7 +103,8 @@ describe(getName(), () => { 'sbt-coursier_2.12_1.0.0-M5/\n' + 'sbt-coursier_2.12_1.0.0-M6/\n' ); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get('/maven2/io/get-coursier/sbt-coursier_2.12_1.0/') .reply( 200, @@ -101,7 +113,8 @@ describe(getName(), () => { '2.0.0-RC6-2/\n' + '2.0.0-RC6-6/\n' ); - nock('https://repo.maven.apache.org') + httpMock + .scope('https://repo.maven.apache.org') .get( '/maven2/io/get-coursier/sbt-coursier_2.12_1.0/2.0.0-RC6-6/sbt-coursier-2.0.0-RC6-6.pom' ) @@ -116,9 +129,8 @@ describe(getName(), () => { ); }); - afterEach(() => { - nock.enableNetConnect(); - }); + // TODO: fix mocks + afterEach(() => httpMock.clear(false)); it('returns null in case of errors', async () => { expect( @@ -138,6 +150,7 @@ describe(getName(), () => { }) ).toBeNull(); }); + it('fetches sbt plugins', async () => { expect( await getPkgReleases({ @@ -152,6 +165,8 @@ describe(getName(), () => { registryUrl: 'https://dl.bintray.com/sbt/sbt-plugin-releases', releases: [{ version: '0.5.5' }], }); + }); + it('fetches sbt plugins 2', async () => { expect( await getPkgReleases({ versioning: mavenVersioning.id, diff --git a/lib/datasource/terraform-module/index.spec.ts b/lib/datasource/terraform-module/index.spec.ts index 224749e1b7920a..d93b9a67da4f43 100644 --- a/lib/datasource/terraform-module/index.spec.ts +++ b/lib/datasource/terraform-module/index.spec.ts @@ -16,11 +16,6 @@ describe(getName(), () => { describe('getReleases', () => { beforeEach(() => { jest.clearAllMocks(); - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); }); it('returns null for empty result', async () => { diff --git a/lib/datasource/terraform-provider/__fixtures__/azurerm-provider.json b/lib/datasource/terraform-provider/__fixtures__/azurerm-provider.json index 749db58549e045..da14abd0f33161 100644 --- a/lib/datasource/terraform-provider/__fixtures__/azurerm-provider.json +++ b/lib/datasource/terraform-provider/__fixtures__/azurerm-provider.json @@ -1 +1,3524 @@ -{"id":"hashicorp/azurerm/1.37.0","owner":"hashicorp","namespace":"hashicorp","name":"azurerm","alias":"azurerm","version":"1.37.0","tag":"v1.37.0","description":"terraform-provider-azurerm","source":"https://github.com/terraform-providers/terraform-provider-azurerm","published_at":"2019-11-26T08:22:56Z","downloads":5181001,"official":true,"versions":["0.1.0","0.1.1","0.1.2","0.1.3","0.1.4","0.1.5","0.1.6","0.1.7","0.2.0","0.2.1","0.2.2","0.3.0","0.3.1","0.3.2","0.3.3","1.0.0","1.0.1","1.1.0","1.1.1","1.1.2","1.2.0","1.3.0","1.3.1","1.3.2","1.3.3","1.4.0","1.5.0","1.6.0","1.7.0","1.8.0","1.9.0","1.10.0","1.11.0","1.12.0","1.13.0","1.14.0","1.15.0","1.16.0","1.17.0","1.18.0","1.19.0","1.20.0","1.21.0","1.22.0","1.22.1","1.23.0","1.24.0","1.25.0","1.26.0","1.27.0","1.27.1","1.28.0","1.29.0","1.30.0","1.30.1","1.31.0","1.32.0","1.32.1","1.33.0","1.33.1","1.34.0","1.35.0","1.36.0","1.36.1","1.37.0"],"docs":[{"id":"27489","title":"overview","path":"website/docs/index.html.markdown","slug":"index","category":"overview","subcategory":""},{"id":"27490","title":"api_management","path":"website/docs/d/api_management.html.markdown","slug":"api_management","category":"data-sources","subcategory":""},{"id":"27491","title":"api_management_api","path":"website/docs/d/api_management_api.html.markdown","slug":"api_management_api","category":"data-sources","subcategory":""},{"id":"27492","title":"api_management_group","path":"website/docs/d/api_management_group.html.markdown","slug":"api_management_group","category":"data-sources","subcategory":""},{"id":"27493","title":"api_management_product","path":"website/docs/d/api_management_product.html.markdown","slug":"api_management_product","category":"data-sources","subcategory":""},{"id":"27494","title":"api_management_user","path":"website/docs/d/api_management_user.html.markdown","slug":"api_management_user","category":"data-sources","subcategory":""},{"id":"27495","title":"app_service","path":"website/docs/d/app_service.html.markdown","slug":"app_service","category":"data-sources","subcategory":""},{"id":"27496","title":"app_service_certificate","path":"website/docs/d/app_service_certificate.html.markdown","slug":"app_service_certificate","category":"data-sources","subcategory":""},{"id":"27497","title":"app_service_certificate_order","path":"website/docs/d/app_service_certificate_order.html.markdown","slug":"app_service_certificate_order","category":"data-sources","subcategory":""},{"id":"27498","title":"app_service_plan","path":"website/docs/d/app_service_plan.html.markdown","slug":"app_service_plan","category":"data-sources","subcategory":""},{"id":"27499","title":"application_insights","path":"website/docs/d/application_insights.html.markdown","slug":"application_insights","category":"data-sources","subcategory":""},{"id":"27500","title":"application_security_group","path":"website/docs/d/application_security_group.html.markdown","slug":"application_security_group","category":"data-sources","subcategory":""},{"id":"27501","title":"automation_account","path":"website/docs/d/automation_account.html.markdown","slug":"automation_account","category":"data-sources","subcategory":""},{"id":"27502","title":"automation_variable_bool","path":"website/docs/d/automation_variable_bool.html.markdown","slug":"automation_variable_bool","category":"data-sources","subcategory":""},{"id":"27503","title":"automation_variable_datetime","path":"website/docs/d/automation_variable_datetime.html.markdown","slug":"automation_variable_datetime","category":"data-sources","subcategory":""},{"id":"27504","title":"automation_variable_int","path":"website/docs/d/automation_variable_int.html.markdown","slug":"automation_variable_int","category":"data-sources","subcategory":""},{"id":"27505","title":"automation_variable_string","path":"website/docs/d/automation_variable_string.html.markdown","slug":"automation_variable_string","category":"data-sources","subcategory":""},{"id":"27506","title":"availability_set","path":"website/docs/d/availability_set.html.markdown","slug":"availability_set","category":"data-sources","subcategory":""},{"id":"27507","title":"azuread_application","path":"website/docs/d/azuread_application.html.markdown","slug":"azuread_application","category":"data-sources","subcategory":""},{"id":"27508","title":"azuread_service_principal","path":"website/docs/d/azuread_service_principal.html.markdown","slug":"azuread_service_principal","category":"data-sources","subcategory":""},{"id":"27509","title":"batch_account","path":"website/docs/d/batch_account.html.markdown","slug":"batch_account","category":"data-sources","subcategory":""},{"id":"27510","title":"batch_certificate","path":"website/docs/d/batch_certificate.html.markdown","slug":"batch_certificate","category":"data-sources","subcategory":""},{"id":"27511","title":"batch_pool","path":"website/docs/d/batch_pool.html.markdown","slug":"batch_pool","category":"data-sources","subcategory":""},{"id":"27512","title":"builtin_role_definition","path":"website/docs/d/builtin_role_definition.markdown","slug":"builtin_role_definition","category":"data-sources","subcategory":""},{"id":"27513","title":"cdn_profile","path":"website/docs/d/cdn_profile.html.markdown","slug":"cdn_profile","category":"data-sources","subcategory":""},{"id":"27514","title":"client_config","path":"website/docs/d/client_config.html.markdown","slug":"client_config","category":"data-sources","subcategory":""},{"id":"27515","title":"container_registry","path":"website/docs/d/container_registry.markdown","slug":"container_registry","category":"data-sources","subcategory":""},{"id":"27516","title":"cosmosdb_account","path":"website/docs/d/cosmosdb_account.html.markdown","slug":"cosmosdb_account","category":"data-sources","subcategory":""},{"id":"27517","title":"data_factory","path":"website/docs/d/data_factory.html.markdown","slug":"data_factory","category":"data-sources","subcategory":""},{"id":"27518","title":"data_lake_store","path":"website/docs/d/data_lake_store.html.markdown","slug":"data_lake_store","category":"data-sources","subcategory":""},{"id":"27519","title":"dev_test_lab","path":"website/docs/d/dev_test_lab.html.markdown","slug":"dev_test_lab","category":"data-sources","subcategory":""},{"id":"27520","title":"dev_test_virtual_network","path":"website/docs/d/dev_test_virtual_network.html.markdown","slug":"dev_test_virtual_network","category":"data-sources","subcategory":""},{"id":"27521","title":"dns_zone","path":"website/docs/d/dns_zone.html.markdown","slug":"dns_zone","category":"data-sources","subcategory":""},{"id":"27522","title":"eventhub_namespace","path":"website/docs/d/eventhub_namespace.html.markdown","slug":"eventhub_namespace","category":"data-sources","subcategory":""},{"id":"27523","title":"express_route_circuit","path":"website/docs/d/express_route_circuit.html.markdown","slug":"express_route_circuit","category":"data-sources","subcategory":""},{"id":"27524","title":"firewall","path":"website/docs/d/firewall.html.markdown","slug":"firewall","category":"data-sources","subcategory":""},{"id":"27525","title":"hdinsight_cluster","path":"website/docs/d/hdinsight_cluster.html.markdown","slug":"hdinsight_cluster","category":"data-sources","subcategory":""},{"id":"27526","title":"healthcare_service","path":"website/docs/d/healthcare_service.html.markdown","slug":"healthcare_service","category":"data-sources","subcategory":""},{"id":"27527","title":"image","path":"website/docs/d/image.html.markdown","slug":"image","category":"data-sources","subcategory":""},{"id":"27528","title":"key_vault","path":"website/docs/d/key_vault.html.markdown","slug":"key_vault","category":"data-sources","subcategory":""},{"id":"27529","title":"key_vault_access_policy","path":"website/docs/d/key_vault_access_policy.html.markdown","slug":"key_vault_access_policy","category":"data-sources","subcategory":""},{"id":"27530","title":"key_vault_key","path":"website/docs/d/key_vault_key.html.markdown","slug":"key_vault_key","category":"data-sources","subcategory":""},{"id":"27531","title":"key_vault_secret","path":"website/docs/d/key_vault_secret.html.markdown","slug":"key_vault_secret","category":"data-sources","subcategory":""},{"id":"27532","title":"kubernetes_cluster","path":"website/docs/d/kubernetes_cluster.html.markdown","slug":"kubernetes_cluster","category":"data-sources","subcategory":""},{"id":"27533","title":"kubernetes_service_versions","path":"website/docs/d/kubernetes_service_versions.html.markdown","slug":"kubernetes_service_versions","category":"data-sources","subcategory":""},{"id":"27534","title":"loadbalancer","path":"website/docs/d/loadbalancer.html.markdown","slug":"loadbalancer","category":"data-sources","subcategory":""},{"id":"27535","title":"loadbalancer_backend_address_pool","path":"website/docs/d/loadbalancer_backend_address_pool.html.markdown","slug":"loadbalancer_backend_address_pool","category":"data-sources","subcategory":""},{"id":"27536","title":"log_analytics_workspace","path":"website/docs/d/log_analytics_workspace.html.markdown","slug":"log_analytics_workspace","category":"data-sources","subcategory":""},{"id":"27537","title":"logic_app_workflow","path":"website/docs/d/logic_app_workflow.html.markdown","slug":"logic_app_workflow","category":"data-sources","subcategory":""},{"id":"27538","title":"managed_disk","path":"website/docs/d/managed_disk.html.markdown","slug":"managed_disk","category":"data-sources","subcategory":""},{"id":"27539","title":"management_group","path":"website/docs/d/management_group.html.markdown","slug":"management_group","category":"data-sources","subcategory":""},{"id":"27540","title":"maps_account","path":"website/docs/d/maps_account.html.markdown","slug":"maps_account","category":"data-sources","subcategory":""},{"id":"27541","title":"monitor_action_group","path":"website/docs/d/monitor_action_group.html.markdown","slug":"monitor_action_group","category":"data-sources","subcategory":""},{"id":"27542","title":"monitor_diagnostic_categories","path":"website/docs/d/monitor_diagnostic_categories.html.markdown","slug":"monitor_diagnostic_categories","category":"data-sources","subcategory":""},{"id":"27543","title":"monitor_log_profile","path":"website/docs/d/monitor_log_profile.html.markdown","slug":"monitor_log_profile","category":"data-sources","subcategory":""},{"id":"27544","title":"mssql_elasticpool","path":"website/docs/d/mssql_elasticpool.html.markdown","slug":"mssql_elasticpool","category":"data-sources","subcategory":""},{"id":"27545","title":"netapp_account","path":"website/docs/d/netapp_account.html.markdown","slug":"netapp_account","category":"data-sources","subcategory":"NetApp"},{"id":"27546","title":"netapp_pool","path":"website/docs/d/netapp_pool.html.markdown","slug":"netapp_pool","category":"data-sources","subcategory":""},{"id":"27547","title":"network_ddos_protection_plan","path":"website/docs/d/network_ddos_protection_plan.html.markdown","slug":"network_ddos_protection_plan","category":"data-sources","subcategory":""},{"id":"27548","title":"network_interface","path":"website/docs/d/network_interface.html.markdown","slug":"network_interface","category":"data-sources","subcategory":""},{"id":"27549","title":"network_security_group","path":"website/docs/d/network_security_group.html.markdown","slug":"network_security_group","category":"data-sources","subcategory":""},{"id":"27550","title":"network_watcher","path":"website/docs/d/network_watcher.html.markdown","slug":"network_watcher","category":"data-sources","subcategory":""},{"id":"27551","title":"notification_hub","path":"website/docs/d/notification_hub.html.markdown","slug":"notification_hub","category":"data-sources","subcategory":""},{"id":"27552","title":"notification_hub_namespace","path":"website/docs/d/notification_hub_namespace.html.markdown","slug":"notification_hub_namespace","category":"data-sources","subcategory":""},{"id":"27553","title":"platform_image","path":"website/docs/d/platform_image.html.markdown","slug":"platform_image","category":"data-sources","subcategory":""},{"id":"27554","title":"policy_definition","path":"website/docs/d/policy_definition.markdown","slug":"policy_definition","category":"data-sources","subcategory":""},{"id":"27555","title":"postgresql_server","path":"website/docs/d/postgresql_server.html.markdown","slug":"postgresql_server","category":"data-sources","subcategory":""},{"id":"27556","title":"private_link_service","path":"website/docs/d/private_link_service.html.markdown","slug":"private_link_service","category":"data-sources","subcategory":""},{"id":"27557","title":"private_link_service_endpoint_connections","path":"website/docs/d/private_link_service_endpoint_connections.html.markdown","slug":"private_link_service_endpoint_connections","category":"data-sources","subcategory":""},{"id":"27558","title":"proximity_placement_group","path":"website/docs/d/proximity_placement_group.html.markdown","slug":"proximity_placement_group","category":"data-sources","subcategory":""},{"id":"27559","title":"public_ip","path":"website/docs/d/public_ip.html.markdown","slug":"public_ip","category":"data-sources","subcategory":""},{"id":"27560","title":"public_ip_prefix","path":"website/docs/d/public_ip_prefix.html.markdown","slug":"public_ip_prefix","category":"data-sources","subcategory":""},{"id":"27561","title":"public_ips","path":"website/docs/d/public_ips.html.markdown","slug":"public_ips","category":"data-sources","subcategory":""},{"id":"27562","title":"recovery_services_protection_policy_vm","path":"website/docs/d/recovery_services_protection_policy_vm.markdown","slug":"recovery_services_protection_policy_vm","category":"data-sources","subcategory":""},{"id":"27563","title":"recovery_services_vault","path":"website/docs/d/recovery_services_vault.markdown","slug":"recovery_services_vault","category":"data-sources","subcategory":""},{"id":"27564","title":"redis_cache","path":"website/docs/d/redis_cache.html.markdown","slug":"redis_cache","category":"data-sources","subcategory":""},{"id":"27565","title":"resource_group","path":"website/docs/d/resource_group.html.markdown","slug":"resource_group","category":"data-sources","subcategory":""},{"id":"27566","title":"resources","path":"website/docs/d/resources.html.markdown","slug":"resources","category":"data-sources","subcategory":""},{"id":"27567","title":"role_definition","path":"website/docs/d/role_definition.markdown","slug":"role_definition","category":"data-sources","subcategory":""},{"id":"27568","title":"route_table","path":"website/docs/d/route_table.html.markdown","slug":"route_table","category":"data-sources","subcategory":""},{"id":"27569","title":"scheduler_job_collection","path":"website/docs/d/scheduler_job_collection.html.markdown","slug":"scheduler_job_collection","category":"data-sources","subcategory":""},{"id":"27570","title":"servicebus_namespace","path":"website/docs/d/servicebus_namespace.html.markdown","slug":"servicebus_namespace","category":"data-sources","subcategory":""},{"id":"27571","title":"servicebus_namespace_authorization_rule","path":"website/docs/d/servicebus_namespace_authorization_rule.html.markdown","slug":"servicebus_namespace_authorization_rule","category":"data-sources","subcategory":""},{"id":"27572","title":"shared_image","path":"website/docs/d/shared_image.html.markdown","slug":"shared_image","category":"data-sources","subcategory":""},{"id":"27573","title":"shared_image_gallery","path":"website/docs/d/shared_image_gallery.html.markdown","slug":"shared_image_gallery","category":"data-sources","subcategory":""},{"id":"27574","title":"shared_image_version","path":"website/docs/d/shared_image_version.html.markdown","slug":"shared_image_version","category":"data-sources","subcategory":""},{"id":"27575","title":"snapshot","path":"website/docs/d/snapshot.html.markdown","slug":"snapshot","category":"data-sources","subcategory":""},{"id":"27576","title":"sql_database","path":"website/docs/d/sql_database.html.markdown","slug":"sql_database","category":"data-sources","subcategory":""},{"id":"27577","title":"sql_server","path":"website/docs/d/sql_server.html.markdown","slug":"sql_server","category":"data-sources","subcategory":""},{"id":"27578","title":"storage_account","path":"website/docs/d/storage_account.html.markdown","slug":"storage_account","category":"data-sources","subcategory":""},{"id":"27579","title":"storage_account_blob_container_sas","path":"website/docs/d/storage_account_blob_container_sas.html.markdown","slug":"storage_account_blob_container_sas","category":"data-sources","subcategory":""},{"id":"27580","title":"storage_account_sas","path":"website/docs/d/storage_account_sas.html.markdown","slug":"storage_account_sas","category":"data-sources","subcategory":""},{"id":"27581","title":"storage_management_policy","path":"website/docs/d/storage_management_policy.html.markdown","slug":"storage_management_policy","category":"data-sources","subcategory":""},{"id":"27582","title":"stream_analytics_job","path":"website/docs/d/stream_analytics_job.html.markdown","slug":"stream_analytics_job","category":"data-sources","subcategory":""},{"id":"27583","title":"subnet","path":"website/docs/d/subnet.html.markdown","slug":"subnet","category":"data-sources","subcategory":""},{"id":"27584","title":"subscription","path":"website/docs/d/subscription.html.markdown","slug":"subscription","category":"data-sources","subcategory":""},{"id":"27585","title":"subscriptions","path":"website/docs/d/subscriptions.html.markdown","slug":"subscriptions","category":"data-sources","subcategory":""},{"id":"27586","title":"traffic_manager_geographical_location","path":"website/docs/d/traffic_manager_geographical_location.html.markdown","slug":"traffic_manager_geographical_location","category":"data-sources","subcategory":""},{"id":"27587","title":"user_assigned_identity","path":"website/docs/d/user_assigned_identity.html.markdown","slug":"user_assigned_identity","category":"data-sources","subcategory":""},{"id":"27588","title":"virtual_hub","path":"website/docs/d/virtual_hub.html.markdown","slug":"virtual_hub","category":"data-sources","subcategory":"Network"},{"id":"27589","title":"virtual_machine","path":"website/docs/d/virtual_machine.html.markdown","slug":"virtual_machine","category":"data-sources","subcategory":""},{"id":"27590","title":"virtual_network","path":"website/docs/d/virtual_network.html.markdown","slug":"virtual_network","category":"data-sources","subcategory":""},{"id":"27591","title":"virtual_network_gateway","path":"website/docs/d/virtual_network_gateway.html.markdown","slug":"virtual_network_gateway","category":"data-sources","subcategory":""},{"id":"27592","title":"virtual_network_gateway_connection","path":"website/docs/d/virtual_network_gateway_connection.html.markdown","slug":"virtual_network_gateway_connection","category":"data-sources","subcategory":""},{"id":"27593","title":"Azure Resource Manager: 2.0 Upgrade Guide","path":"website/docs/guides/2.0-upgrade-guide.html.markdown","slug":"2.0-upgrade-guide","category":"guides","subcategory":""},{"id":"27594","title":"Azure Provider: Authenticating via the Azure CLI","path":"website/docs/guides/azure_cli.html.markdown","slug":"azure_cli","category":"guides","subcategory":""},{"id":"27595","title":"Azure Provider: Authenticating via Managed Identity","path":"website/docs/guides/managed_service_identity.html.markdown","slug":"managed_service_identity","category":"guides","subcategory":""},{"id":"27596","title":"Azure Provider: Migrating to a renamed resource","path":"website/docs/guides/migrating-between-renamed-resources.html.markdown","slug":"migrating-between-renamed-resources","category":"guides","subcategory":""},{"id":"27597","title":"Azure Active Directory: Migrating to the AzureAD Provider","path":"website/docs/guides/migrating-to-azuread.html.markdown","slug":"migrating-to-azuread","category":"guides","subcategory":""},{"id":"27598","title":"Azure Provider: Authenticating via a Service Principal and a Client Certificate","path":"website/docs/guides/service_principal_client_certificate.html.markdown","slug":"service_principal_client_certificate","category":"guides","subcategory":""},{"id":"27599","title":"Azure Provider: Authenticating via a Service Principal and a Client Secret","path":"website/docs/guides/service_principal_client_secret.html.markdown","slug":"service_principal_client_secret","category":"guides","subcategory":""},{"id":"27600","title":"analysis_services_server","path":"website/docs/r/analysis_services_server.html.markdown","slug":"analysis_services_server","category":"resources","subcategory":"Analysis Services"},{"id":"27601","title":"api_management","path":"website/docs/r/api_management.html.markdown","slug":"api_management","category":"resources","subcategory":"API Management"},{"id":"27602","title":"api_management_api","path":"website/docs/r/api_management_api.html.markdown","slug":"api_management_api","category":"resources","subcategory":"API Management"},{"id":"27603","title":"api_management_api_operation","path":"website/docs/r/api_management_api_operation.html.markdown","slug":"api_management_api_operation","category":"resources","subcategory":"API Management"},{"id":"27604","title":"api_management_api_operation_policy","path":"website/docs/r/api_management_api_operation_policy.html.markdown","slug":"api_management_api_operation_policy","category":"resources","subcategory":"API Management"},{"id":"27605","title":"api_management_api_policy","path":"website/docs/r/api_management_api_policy.html.markdown","slug":"api_management_api_policy","category":"resources","subcategory":"API Management"},{"id":"27606","title":"api_management_api_schema","path":"website/docs/r/api_management_api_schema.html.markdown","slug":"api_management_api_schema","category":"resources","subcategory":"API Management"},{"id":"27607","title":"api_management_api_version_set","path":"website/docs/r/api_management_api_version_set.html.markdown","slug":"api_management_api_version_set","category":"resources","subcategory":"API Management"},{"id":"27608","title":"api_management_authorization_server","path":"website/docs/r/api_management_authorization_server.html.markdown","slug":"api_management_authorization_server","category":"resources","subcategory":"API Management"},{"id":"27609","title":"api_management_backend","path":"website/docs/r/api_management_backend.html.markdown","slug":"api_management_backend","category":"resources","subcategory":"API Management"},{"id":"27610","title":"api_management_certificate","path":"website/docs/r/api_management_certificate.html.markdown","slug":"api_management_certificate","category":"resources","subcategory":"API Management"},{"id":"27611","title":"api_management_group","path":"website/docs/r/api_management_group.html.markdown","slug":"api_management_group","category":"resources","subcategory":"API Management"},{"id":"27612","title":"api_management_group_user","path":"website/docs/r/api_management_group_user.html.markdown","slug":"api_management_group_user","category":"resources","subcategory":"API Management"},{"id":"27613","title":"api_management_logger","path":"website/docs/r/api_management_logger.html.markdown","slug":"api_management_logger","category":"resources","subcategory":"API Management"},{"id":"27614","title":"api_management_openid_connect_provider","path":"website/docs/r/api_management_openid_connect_provider.html.markdown","slug":"api_management_openid_connect_provider","category":"resources","subcategory":"API Management"},{"id":"27615","title":"api_management_product","path":"website/docs/r/api_management_product.html.markdown","slug":"api_management_product","category":"resources","subcategory":"API Management"},{"id":"27616","title":"api_management_product_api","path":"website/docs/r/api_management_product_api.html.markdown","slug":"api_management_product_api","category":"resources","subcategory":"API Management"},{"id":"27617","title":"api_management_product_group","path":"website/docs/r/api_management_product_group.html.markdown","slug":"api_management_product_group","category":"resources","subcategory":"API Management"},{"id":"27618","title":"api_management_product_policy","path":"website/docs/r/api_management_product_policy.html.markdown","slug":"api_management_product_policy","category":"resources","subcategory":"API Management"},{"id":"27619","title":"api_management_property","path":"website/docs/r/api_management_property.html.markdown","slug":"api_management_property","category":"resources","subcategory":"API Management"},{"id":"27620","title":"api_management_subscription","path":"website/docs/r/api_management_subscription.html.markdown","slug":"api_management_subscription","category":"resources","subcategory":"API Management"},{"id":"27621","title":"api_management_user","path":"website/docs/r/api_management_user.html.markdown","slug":"api_management_user","category":"resources","subcategory":"API Management"},{"id":"27622","title":"app_service","path":"website/docs/r/app_service.html.markdown","slug":"app_service","category":"resources","subcategory":"App Service (Web Apps)"},{"id":"27623","title":"app_service_active_slot","path":"website/docs/r/app_service_active_slot.html.markdown","slug":"app_service_active_slot","category":"resources","subcategory":"App Service (Web Apps)"},{"id":"27624","title":"app_service_certificate","path":"website/docs/r/app_service_certificate.html.markdown","slug":"app_service_certificate","category":"resources","subcategory":"App Service (Web Apps)"},{"id":"27625","title":"app_service_certificate_order","path":"website/docs/r/app_service_certificate_order.html.markdown","slug":"app_service_certificate_order","category":"resources","subcategory":"App Service (Web Apps)"},{"id":"27626","title":"app_service_custom_hostname_binding","path":"website/docs/r/app_service_custom_hostname_binding.html.markdown","slug":"app_service_custom_hostname_binding","category":"resources","subcategory":"App Service (Web Apps)"},{"id":"27627","title":"app_service_plan","path":"website/docs/r/app_service_plan.html.markdown","slug":"app_service_plan","category":"resources","subcategory":"App Service (Web Apps)"},{"id":"27628","title":"app_service_slot","path":"website/docs/r/app_service_slot.html.markdown","slug":"app_service_slot","category":"resources","subcategory":"App Service (Web Apps)"},{"id":"27629","title":"app_service_source_control_token","path":"website/docs/r/app_service_source_control_token.html.markdown","slug":"app_service_source_control_token","category":"resources","subcategory":"App Service (Web Apps)"},{"id":"27630","title":"application_gateway","path":"website/docs/r/application_gateway.html.markdown","slug":"application_gateway","category":"resources","subcategory":"Network"},{"id":"27631","title":"application_insights","path":"website/docs/r/application_insights.html.markdown","slug":"application_insights","category":"resources","subcategory":"Application Insights"},{"id":"27632","title":"application_insights_analytics_item","path":"website/docs/r/application_insights_analytics_item.html.markdown","slug":"application_insights_analytics_item","category":"resources","subcategory":"Application Insights"},{"id":"27633","title":"application_insights_api_key","path":"website/docs/r/application_insights_api_key.html.markdown","slug":"application_insights_api_key","category":"resources","subcategory":"Application Insights"},{"id":"27634","title":"application_insights_web_test","path":"website/docs/r/application_insights_web_test.html.markdown","slug":"application_insights_web_test","category":"resources","subcategory":"Application Insights"},{"id":"27635","title":"application_security_group","path":"website/docs/r/application_security_group.html.markdown","slug":"application_security_group","category":"resources","subcategory":"Network"},{"id":"27636","title":"automation_account","path":"website/docs/r/automation_account.html.markdown","slug":"automation_account","category":"resources","subcategory":"Automation"},{"id":"27637","title":"automation_credential","path":"website/docs/r/automation_credential.html.markdown","slug":"automation_credential","category":"resources","subcategory":"Automation"},{"id":"27638","title":"automation_dsc_configuration","path":"website/docs/r/automation_dsc_configuration.html.markdown","slug":"automation_dsc_configuration","category":"resources","subcategory":"Automation"},{"id":"27639","title":"automation_dsc_nodeconfiguration","path":"website/docs/r/automation_dsc_nodeconfiguration.html.markdown","slug":"automation_dsc_nodeconfiguration","category":"resources","subcategory":"Automation"},{"id":"27640","title":"automation_job_schedule","path":"website/docs/r/automation_job_schedule.html.markdown","slug":"automation_job_schedule","category":"resources","subcategory":"Automation"},{"id":"27641","title":"automation_module","path":"website/docs/r/automation_module.html.markdown","slug":"automation_module","category":"resources","subcategory":"Automation"},{"id":"27642","title":"automation_runbook","path":"website/docs/r/automation_runbook.html.markdown","slug":"automation_runbook","category":"resources","subcategory":"Automation"},{"id":"27643","title":"automation_schedule","path":"website/docs/r/automation_schedule.html.markdown","slug":"automation_schedule","category":"resources","subcategory":"Automation"},{"id":"27644","title":"automation_variable_bool","path":"website/docs/r/automation_variable_bool.html.markdown","slug":"automation_variable_bool","category":"resources","subcategory":"Automation"},{"id":"27645","title":"automation_variable_datetime","path":"website/docs/r/automation_variable_datetime.html.markdown","slug":"automation_variable_datetime","category":"resources","subcategory":"Automation"},{"id":"27646","title":"automation_variable_int","path":"website/docs/r/automation_variable_int.html.markdown","slug":"automation_variable_int","category":"resources","subcategory":"Automation"},{"id":"27647","title":"automation_variable_string","path":"website/docs/r/automation_variable_string.html.markdown","slug":"automation_variable_string","category":"resources","subcategory":"Automation"},{"id":"27648","title":"autoscale_setting","path":"website/docs/r/autoscale_setting.html.markdown","slug":"autoscale_setting","category":"resources","subcategory":"Monitor"},{"id":"27649","title":"availability_set","path":"website/docs/r/availability_set.html.markdown","slug":"availability_set","category":"resources","subcategory":"Compute"},{"id":"27650","title":"azuread_application","path":"website/docs/r/azuread_application.html.markdown","slug":"azuread_application","category":"resources","subcategory":"Azure Active Directory"},{"id":"27651","title":"azuread_service_principal","path":"website/docs/r/azuread_service_principal.html.markdown","slug":"azuread_service_principal","category":"resources","subcategory":"Azure Active Directory"},{"id":"27652","title":"azuread_service_principal_password","path":"website/docs/r/azuread_service_principal_password.html.markdown","slug":"azuread_service_principal_password","category":"resources","subcategory":"Azure Active Directory"},{"id":"27653","title":"bastion_host","path":"website/docs/r/bastion_host.html.markdown","slug":"bastion_host","category":"resources","subcategory":"Network"},{"id":"27654","title":"batch_account","path":"website/docs/r/batch_account.html.markdown","slug":"batch_account","category":"resources","subcategory":"Batch"},{"id":"27655","title":"batch_application","path":"website/docs/r/batch_application.html.markdown","slug":"batch_application","category":"resources","subcategory":"Batch"},{"id":"27656","title":"batch_certificate","path":"website/docs/r/batch_certificate.html.markdown","slug":"batch_certificate","category":"resources","subcategory":"Batch"},{"id":"27657","title":"batch_pool","path":"website/docs/r/batch_pool.html.markdown","slug":"batch_pool","category":"resources","subcategory":"Batch"},{"id":"27658","title":"bot_channel_email","path":"website/docs/r/bot_channel_email.markdown","slug":"bot_channel_email","category":"resources","subcategory":"Bot"},{"id":"27659","title":"bot_channel_slack","path":"website/docs/r/bot_channel_slack.markdown","slug":"bot_channel_slack","category":"resources","subcategory":"Bot"},{"id":"27660","title":"bot_channels_registration","path":"website/docs/r/bot_channels_registration.markdown","slug":"bot_channels_registration","category":"resources","subcategory":"Bot"},{"id":"27661","title":"bot_connection","path":"website/docs/r/bot_connection.markdown","slug":"bot_connection","category":"resources","subcategory":"Bot"},{"id":"27662","title":"bot_web_app","path":"website/docs/r/bot_web_app.markdown","slug":"bot_web_app","category":"resources","subcategory":"Bot"},{"id":"27663","title":"cdn_endpoint","path":"website/docs/r/cdn_endpoint.html.markdown","slug":"cdn_endpoint","category":"resources","subcategory":"CDN"},{"id":"27664","title":"cdn_profile","path":"website/docs/r/cdn_profile.html.markdown","slug":"cdn_profile","category":"resources","subcategory":"CDN"},{"id":"27665","title":"cognitive_account","path":"website/docs/r/cognitive_account.html.markdown","slug":"cognitive_account","category":"resources","subcategory":"Cognitive Services"},{"id":"27666","title":"connection_monitor","path":"website/docs/r/connection_monitor.html.markdown","slug":"connection_monitor","category":"resources","subcategory":"Network"},{"id":"27667","title":"container_group","path":"website/docs/r/container_group.html.markdown","slug":"container_group","category":"resources","subcategory":"Container"},{"id":"27668","title":"container_registry","path":"website/docs/r/container_registry.html.markdown","slug":"container_registry","category":"resources","subcategory":"Container"},{"id":"27669","title":"container_registry_webhook","path":"website/docs/r/container_registry_webhook.html.markdown","slug":"container_registry_webhook","category":"resources","subcategory":"Container"},{"id":"27670","title":"container_service","path":"website/docs/r/container_service.html.markdown","slug":"container_service","category":"resources","subcategory":"Container"},{"id":"27671","title":"cosmosdb_account","path":"website/docs/r/cosmosdb_account.html.markdown","slug":"cosmosdb_account","category":"resources","subcategory":"CosmosDB (DocumentDB)"},{"id":"27672","title":"cosmosdb_cassandra_keyspace","path":"website/docs/r/cosmosdb_cassandra_keyspace.html.markdown","slug":"cosmosdb_cassandra_keyspace","category":"resources","subcategory":"CosmosDB (DocumentDB)"},{"id":"27673","title":"cosmosdb_mongo_collection","path":"website/docs/r/cosmosdb_mongo_collection.html.markdown","slug":"cosmosdb_mongo_collection","category":"resources","subcategory":"CosmosDB (DocumentDB)"},{"id":"27674","title":"cosmosdb_mongo_database","path":"website/docs/r/cosmosdb_mongo_database.html.markdown","slug":"cosmosdb_mongo_database","category":"resources","subcategory":"CosmosDB (DocumentDB)"},{"id":"27675","title":"cosmosdb_sql_container","path":"website/docs/r/cosmosdb_sql_container.html.markdown","slug":"cosmosdb_sql_container","category":"resources","subcategory":"CosmosDB (DocumentDB)"},{"id":"27676","title":"cosmosdb_sql_database","path":"website/docs/r/cosmosdb_sql_database.html.markdown","slug":"cosmosdb_sql_database","category":"resources","subcategory":"CosmosDB (DocumentDB)"},{"id":"27677","title":"cosmosdb_table","path":"website/docs/r/cosmosdb_table.html.markdown","slug":"cosmosdb_table","category":"resources","subcategory":"CosmosDB (DocumentDB)"},{"id":"27678","title":"dashboard","path":"website/docs/r/dashboard.html.markdown","slug":"dashboard","category":"resources","subcategory":"Portal"},{"id":"27679","title":"data_factory","path":"website/docs/r/data_factory.html.markdown","slug":"data_factory","category":"resources","subcategory":"Data Factory"},{"id":"27680","title":"data_factory_dataset_mysql","path":"website/docs/r/data_factory_dataset_mysql.html.markdown","slug":"data_factory_dataset_mysql","category":"resources","subcategory":"Data Factory"},{"id":"27681","title":"data_factory_dataset_postgresql","path":"website/docs/r/data_factory_dataset_postgresql.html.markdown","slug":"data_factory_dataset_postgresql","category":"resources","subcategory":"Data Factory"},{"id":"27682","title":"data_factory_dataset_sql_server_table","path":"website/docs/r/data_factory_dataset_sql_server_table.html.markdown","slug":"data_factory_dataset_sql_server_table","category":"resources","subcategory":"Data Factory"},{"id":"27683","title":"data_factory_integration_runtime_managed","path":"website/docs/r/data_factory_integration_runtime_managed.html.markdown","slug":"data_factory_integration_runtime_managed","category":"resources","subcategory":"Data Factory"},{"id":"27684","title":"data_factory_linked_service_data_lake_storage_gen2","path":"website/docs/r/data_factory_linked_service_data_lake_storage_gen2.html.markdown","slug":"data_factory_linked_service_data_lake_storage_gen2","category":"resources","subcategory":"Data Factory"},{"id":"27685","title":"data_factory_linked_service_mysql","path":"website/docs/r/data_factory_linked_service_mysql.html.markdown","slug":"data_factory_linked_service_mysql","category":"resources","subcategory":"Data Factory"},{"id":"27686","title":"data_factory_linked_service_postgresql","path":"website/docs/r/data_factory_linked_service_postgresql.html.markdown","slug":"data_factory_linked_service_postgresql","category":"resources","subcategory":"Data Factory"},{"id":"27687","title":"data_factory_linked_service_sql_server","path":"website/docs/r/data_factory_linked_service_sql_server.html.markdown","slug":"data_factory_linked_service_sql_server","category":"resources","subcategory":"Data Factory"},{"id":"27688","title":"data_factory_pipeline","path":"website/docs/r/data_factory_pipeline.html.markdown","slug":"data_factory_pipeline","category":"resources","subcategory":"Data Factory"},{"id":"27689","title":"data_factory_trigger_schedule","path":"website/docs/r/data_factory_trigger_schedule.html.markdown","slug":"data_factory_trigger_schedule","category":"resources","subcategory":"Data Factory"},{"id":"27690","title":"data_lake_analytics_account","path":"website/docs/r/data_lake_analytics_account.html.markdown","slug":"data_lake_analytics_account","category":"resources","subcategory":"Data Lake"},{"id":"27691","title":"data_lake_analytics_firewall_rule","path":"website/docs/r/data_lake_analytics_firewall_rule.html.markdown","slug":"data_lake_analytics_firewall_rule","category":"resources","subcategory":"Data Lake"},{"id":"27692","title":"data_lake_store","path":"website/docs/r/data_lake_store.html.markdown","slug":"data_lake_store","category":"resources","subcategory":"Data Lake"},{"id":"27693","title":"data_lake_store_file","path":"website/docs/r/data_lake_store_file.html.markdown","slug":"data_lake_store_file","category":"resources","subcategory":"Data Lake"},{"id":"27694","title":"data_lake_store_firewall_rule","path":"website/docs/r/data_lake_store_firewall_rule.html.markdown","slug":"data_lake_store_firewall_rule","category":"resources","subcategory":"Data Lake"},{"id":"27695","title":"databricks_workspace","path":"website/docs/r/databricks_workspace.html.markdown","slug":"databricks_workspace","category":"resources","subcategory":"Databricks"},{"id":"27696","title":"ddos_protection_plan","path":"website/docs/r/ddos_protection_plan.html.markdown","slug":"ddos_protection_plan","category":"resources","subcategory":"Network"},{"id":"27697","title":"dev_test_lab","path":"website/docs/r/dev_test_lab.html.markdown","slug":"dev_test_lab","category":"resources","subcategory":"Dev Test"},{"id":"27698","title":"dev_test_linux_virtual_machine","path":"website/docs/r/dev_test_linux_virtual_machine.html.markdown","slug":"dev_test_linux_virtual_machine","category":"resources","subcategory":"Dev Test"},{"id":"27699","title":"dev_test_policy","path":"website/docs/r/dev_test_policy.html.markdown","slug":"dev_test_policy","category":"resources","subcategory":"Dev Test"},{"id":"27700","title":"dev_test_schedule","path":"website/docs/r/dev_test_schedule.html.markdown","slug":"dev_test_schedule","category":"resources","subcategory":"Dev Test"},{"id":"27701","title":"dev_test_virtual_network","path":"website/docs/r/dev_test_virtual_network.html.markdown","slug":"dev_test_virtual_network","category":"resources","subcategory":"Dev Test"},{"id":"27702","title":"dev_test_windows_virtual_machine","path":"website/docs/r/dev_test_windows_virtual_machine.html.markdown","slug":"dev_test_windows_virtual_machine","category":"resources","subcategory":"Dev Test"},{"id":"27703","title":"devspace_controller","path":"website/docs/r/devspace_controller.html.markdown","slug":"devspace_controller","category":"resources","subcategory":"DevSpace"},{"id":"27704","title":"dns_a_record","path":"website/docs/r/dns_a_record.html.markdown","slug":"dns_a_record","category":"resources","subcategory":"DNS"},{"id":"27705","title":"dns_aaaa_record","path":"website/docs/r/dns_aaaa_record.html.markdown","slug":"dns_aaaa_record","category":"resources","subcategory":"DNS"},{"id":"27706","title":"dns_caa_record","path":"website/docs/r/dns_caa_record.html.markdown","slug":"dns_caa_record","category":"resources","subcategory":"DNS"},{"id":"27707","title":"dns_cname_record","path":"website/docs/r/dns_cname_record.html.markdown","slug":"dns_cname_record","category":"resources","subcategory":"DNS"},{"id":"27708","title":"dns_mx_record","path":"website/docs/r/dns_mx_record.html.markdown","slug":"dns_mx_record","category":"resources","subcategory":"DNS"},{"id":"27709","title":"dns_ns_record","path":"website/docs/r/dns_ns_record.html.markdown","slug":"dns_ns_record","category":"resources","subcategory":"DNS"},{"id":"27710","title":"dns_ptr_record","path":"website/docs/r/dns_ptr_record.html.markdown","slug":"dns_ptr_record","category":"resources","subcategory":"DNS"},{"id":"27711","title":"dns_srv_record","path":"website/docs/r/dns_srv_record.html.markdown","slug":"dns_srv_record","category":"resources","subcategory":"DNS"},{"id":"27712","title":"dns_txt_record","path":"website/docs/r/dns_txt_record.html.markdown","slug":"dns_txt_record","category":"resources","subcategory":"DNS"},{"id":"27713","title":"dns_zone","path":"website/docs/r/dns_zone.html.markdown","slug":"dns_zone","category":"resources","subcategory":"DNS"},{"id":"27714","title":"eventgrid_domain","path":"website/docs/r/eventgrid_domain.html.markdown","slug":"eventgrid_domain","category":"resources","subcategory":"Messaging"},{"id":"27715","title":"eventgrid_event_subscription","path":"website/docs/r/eventgrid_event_subscription.html.markdown","slug":"eventgrid_event_subscription","category":"resources","subcategory":"Messaging"},{"id":"27716","title":"eventgrid_topic","path":"website/docs/r/eventgrid_topic.html.markdown","slug":"eventgrid_topic","category":"resources","subcategory":"Messaging"},{"id":"27717","title":"eventhub","path":"website/docs/r/eventhub.html.markdown","slug":"eventhub","category":"resources","subcategory":"Messaging"},{"id":"27718","title":"eventhub_authorization_rule","path":"website/docs/r/eventhub_authorization_rule.html.markdown","slug":"eventhub_authorization_rule","category":"resources","subcategory":"Messaging"},{"id":"27719","title":"eventhub_consumer_group","path":"website/docs/r/eventhub_consumer_group.html.markdown","slug":"eventhub_consumer_group","category":"resources","subcategory":"Messaging"},{"id":"27720","title":"eventhub_namespace","path":"website/docs/r/eventhub_namespace.html.markdown","slug":"eventhub_namespace","category":"resources","subcategory":"Messaging"},{"id":"27721","title":"eventhub_namespace_authorization_rule","path":"website/docs/r/eventhub_namespace_authorization_rule.html.markdown","slug":"eventhub_namespace_authorization_rule","category":"resources","subcategory":"Messaging"},{"id":"27722","title":"eventhub_namespace_disaster_recovery_config","path":"website/docs/r/eventhub_namespace_disaster_recovery_config.html.markdown","slug":"eventhub_namespace_disaster_recovery_config","category":"resources","subcategory":"Messaging"},{"id":"27723","title":"express_route_circuit","path":"website/docs/r/express_route_circuit.html.markdown","slug":"express_route_circuit","category":"resources","subcategory":"Network"},{"id":"27724","title":"express_route_circuit_authorization","path":"website/docs/r/express_route_circuit_authorization.html.markdown","slug":"express_route_circuit_authorization","category":"resources","subcategory":"Network"},{"id":"27725","title":"express_route_circuit_peering","path":"website/docs/r/express_route_circuit_peering.html.markdown","slug":"express_route_circuit_peering","category":"resources","subcategory":"Network"},{"id":"27726","title":"firewall","path":"website/docs/r/firewall.html.markdown","slug":"firewall","category":"resources","subcategory":"Network"},{"id":"27727","title":"firewall_application_rule_collection","path":"website/docs/r/firewall_application_rule_collection.html.markdown","slug":"firewall_application_rule_collection","category":"resources","subcategory":"Network"},{"id":"27728","title":"firewall_nat_rule_collection","path":"website/docs/r/firewall_nat_rule_collection.html.markdown","slug":"firewall_nat_rule_collection","category":"resources","subcategory":"Network"},{"id":"27729","title":"firewall_network_rule_collection","path":"website/docs/r/firewall_network_rule_collection.html.markdown","slug":"firewall_network_rule_collection","category":"resources","subcategory":"Network"},{"id":"27730","title":"front_door","path":"website/docs/r/front_door.html.markdown","slug":"front_door","category":"resources","subcategory":"Front Door"},{"id":"27731","title":"front_door_firewall_policy","path":"website/docs/r/front_door_firewall_policy.html.markdown","slug":"front_door_firewall_policy","category":"resources","subcategory":"Front Door"},{"id":"27732","title":"function_app","path":"website/docs/r/function_app.html.markdown","slug":"function_app","category":"resources","subcategory":"App Service (Web Apps)"},{"id":"27733","title":"hdinsight_hadoop_cluster","path":"website/docs/r/hdinsight_hadoop_cluster.html.markdown","slug":"hdinsight_hadoop_cluster","category":"resources","subcategory":"HDInsight"},{"id":"27734","title":"hdinsight_hbase_cluster","path":"website/docs/r/hdinsight_hbase_cluster.html.markdown","slug":"hdinsight_hbase_cluster","category":"resources","subcategory":"HDInsight"},{"id":"27735","title":"hdinsight_interactive_query_cluster","path":"website/docs/r/hdinsight_interactive_query_cluster.html.markdown","slug":"hdinsight_interactive_query_cluster","category":"resources","subcategory":"HDInsight"},{"id":"27736","title":"hdinsight_kafka_cluster","path":"website/docs/r/hdinsight_kafka_cluster.html.markdown","slug":"hdinsight_kafka_cluster","category":"resources","subcategory":"HDInsight"},{"id":"27737","title":"hdinsight_ml_services_cluster","path":"website/docs/r/hdinsight_ml_services_cluster.html.markdown","slug":"hdinsight_ml_services_cluster","category":"resources","subcategory":"HDInsight"},{"id":"27738","title":"hdinsight_rserver_cluster","path":"website/docs/r/hdinsight_rserver_cluster.html.markdown","slug":"hdinsight_rserver_cluster","category":"resources","subcategory":"HDInsight"},{"id":"27739","title":"hdinsight_spark_cluster","path":"website/docs/r/hdinsight_spark_cluster.html.markdown","slug":"hdinsight_spark_cluster","category":"resources","subcategory":"HDInsight"},{"id":"27740","title":"hdinsight_storm_cluster","path":"website/docs/r/hdinsight_storm_cluster.html.markdown","slug":"hdinsight_storm_cluster","category":"resources","subcategory":"HDInsight"},{"id":"27741","title":"healthcare_service","path":"website/docs/r/healthcare_service.html.markdown","slug":"healthcare_service","category":"resources","subcategory":"Healthcare API"},{"id":"27742","title":"image","path":"website/docs/r/image.html.markdown","slug":"image","category":"resources","subcategory":"Compute"},{"id":"27743","title":"iothub","path":"website/docs/r/iothub.html.markdown","slug":"iothub","category":"resources","subcategory":"IoT Hub"},{"id":"27744","title":"iothub_consumer_group","path":"website/docs/r/iothub_consumer_group.html.markdown","slug":"iothub_consumer_group","category":"resources","subcategory":"IoT Hub"},{"id":"27745","title":"iothub_dps","path":"website/docs/r/iothub_dps.html.markdown","slug":"iothub_dps","category":"resources","subcategory":"IoT Hub"},{"id":"27746","title":"iothub_dps_certificate","path":"website/docs/r/iothub_dps_certificate.html.markdown","slug":"iothub_dps_certificate","category":"resources","subcategory":"IoT Hub"},{"id":"27747","title":"iothub_endpoint_eventhub","path":"website/docs/r/iothub_endpoint_eventhub.html.markdown","slug":"iothub_endpoint_eventhub","category":"resources","subcategory":"Messaging"},{"id":"27748","title":"iothub_endpoint_servicebus_queue","path":"website/docs/r/iothub_endpoint_servicebus_queue.html.markdown","slug":"iothub_endpoint_servicebus_queue","category":"resources","subcategory":"Messaging"},{"id":"27749","title":"iothub_endpoint_servicebus_topic","path":"website/docs/r/iothub_endpoint_servicebus_topic.html.markdown","slug":"iothub_endpoint_servicebus_topic","category":"resources","subcategory":"Messaging"},{"id":"27750","title":"iothub_endpoint_storage_container","path":"website/docs/r/iothub_endpoint_storage_container.html.markdown","slug":"iothub_endpoint_storage_container","category":"resources","subcategory":"Messaging"},{"id":"27751","title":"iothub_route","path":"website/docs/r/iothub_route.html.markdown","slug":"iothub_route","category":"resources","subcategory":"Messaging"},{"id":"27752","title":"iothub_shared_access_policy","path":"website/docs/r/iothub_shared_access_policy.html.markdown","slug":"iothub_shared_access_policy","category":"resources","subcategory":"IoT Hub"},{"id":"27753","title":"key_vault","path":"website/docs/r/key_vault.html.markdown","slug":"key_vault","category":"resources","subcategory":"Key Vault"},{"id":"27754","title":"key_vault_access_policy","path":"website/docs/r/key_vault_access_policy.html.markdown","slug":"key_vault_access_policy","category":"resources","subcategory":"Key Vault"},{"id":"27755","title":"key_vault_certificate","path":"website/docs/r/key_vault_certificate.html.markdown","slug":"key_vault_certificate","category":"resources","subcategory":"Key Vault"},{"id":"27756","title":"key_vault_key","path":"website/docs/r/key_vault_key.html.markdown","slug":"key_vault_key","category":"resources","subcategory":"Key Vault"},{"id":"27757","title":"key_vault_secret","path":"website/docs/r/key_vault_secret.html.markdown","slug":"key_vault_secret","category":"resources","subcategory":"Key Vault"},{"id":"27758","title":"kubernetes_cluster","path":"website/docs/r/kubernetes_cluster.html.markdown","slug":"kubernetes_cluster","category":"resources","subcategory":"Container"},{"id":"27759","title":"kubernetes_cluster_node_pool","path":"website/docs/r/kubernetes_cluster_node_pool.html.markdown","slug":"kubernetes_cluster_node_pool","category":"resources","subcategory":"Container"},{"id":"27760","title":"kusto_cluster","path":"website/docs/r/kusto_cluster.html.markdown","slug":"kusto_cluster","category":"resources","subcategory":"Data Explorer"},{"id":"27761","title":"kusto_database","path":"website/docs/r/kusto_database.html.markdown","slug":"kusto_database","category":"resources","subcategory":"Data Explorer"},{"id":"27762","title":"kusto_eventhub_data_connection","path":"website/docs/r/kusto_eventhub_data_connection.html.markdown","slug":"kusto_eventhub_data_connection","category":"resources","subcategory":"Data Explorer"},{"id":"27763","title":"linux_virtual_machine_scale_set","path":"website/docs/r/linux_virtual_machine_scale_set.html.markdown","slug":"linux_virtual_machine_scale_set","category":"resources","subcategory":"Beta"},{"id":"27764","title":"loadbalancer","path":"website/docs/r/loadbalancer.html.markdown","slug":"loadbalancer","category":"resources","subcategory":"Load Balancer"},{"id":"27765","title":"loadbalancer_backend_address_pool","path":"website/docs/r/loadbalancer_backend_address_pool.html.markdown","slug":"loadbalancer_backend_address_pool","category":"resources","subcategory":"Load Balancer"},{"id":"27766","title":"loadbalancer_nat_pool","path":"website/docs/r/loadbalancer_nat_pool.html.markdown","slug":"loadbalancer_nat_pool","category":"resources","subcategory":"Load Balancer"},{"id":"27767","title":"loadbalancer_nat_rule","path":"website/docs/r/loadbalancer_nat_rule.html.markdown","slug":"loadbalancer_nat_rule","category":"resources","subcategory":"Load Balancer"},{"id":"27768","title":"loadbalancer_outbound_rule","path":"website/docs/r/loadbalancer_outbound_rule.html.markdown","slug":"loadbalancer_outbound_rule","category":"resources","subcategory":"Load Balancer"},{"id":"27769","title":"loadbalancer_probe","path":"website/docs/r/loadbalancer_probe.html.markdown","slug":"loadbalancer_probe","category":"resources","subcategory":"Load Balancer"},{"id":"27770","title":"loadbalancer_rule","path":"website/docs/r/loadbalancer_rule.html.markdown","slug":"loadbalancer_rule","category":"resources","subcategory":"Load Balancer"},{"id":"27771","title":"local_network_gateway","path":"website/docs/r/local_network_gateway.html.markdown","slug":"local_network_gateway","category":"resources","subcategory":"Network"},{"id":"27772","title":"log_analytics_linked_service","path":"website/docs/r/log_analytics_linked_service.html.markdown","slug":"log_analytics_linked_service","category":"resources","subcategory":"Log Analytics"},{"id":"27773","title":"log_analytics_solution","path":"website/docs/r/log_analytics_solution.html.markdown","slug":"log_analytics_solution","category":"resources","subcategory":"Log Analytics"},{"id":"27774","title":"log_analytics_workspace","path":"website/docs/r/log_analytics_workspace.html.markdown","slug":"log_analytics_workspace","category":"resources","subcategory":"Log Analytics"},{"id":"27775","title":"log_analytics_workspace_linked_service","path":"website/docs/r/log_analytics_workspace_linked_service.html.markdown","slug":"log_analytics_workspace_linked_service","category":"resources","subcategory":"Log Analytics"},{"id":"27776","title":"logic_app_action_custom","path":"website/docs/r/logic_app_action_custom.html.markdown","slug":"logic_app_action_custom","category":"resources","subcategory":"Logic App"},{"id":"27777","title":"logic_app_action_http","path":"website/docs/r/logic_app_action_http.html.markdown","slug":"logic_app_action_http","category":"resources","subcategory":"Logic App"},{"id":"27778","title":"logic_app_trigger_custom","path":"website/docs/r/logic_app_trigger_custom.html.markdown","slug":"logic_app_trigger_custom","category":"resources","subcategory":"Logic App"},{"id":"27779","title":"logic_app_trigger_http_request","path":"website/docs/r/logic_app_trigger_http_request.html.markdown","slug":"logic_app_trigger_http_request","category":"resources","subcategory":"Logic App"},{"id":"27780","title":"logic_app_trigger_recurrence","path":"website/docs/r/logic_app_trigger_recurrence.html.markdown","slug":"logic_app_trigger_recurrence","category":"resources","subcategory":"Logic App"},{"id":"27781","title":"logic_app_workflow","path":"website/docs/r/logic_app_workflow.html.markdown","slug":"logic_app_workflow","category":"resources","subcategory":"Logic App"},{"id":"27782","title":"managed_disk","path":"website/docs/r/managed_disk.html.markdown","slug":"managed_disk","category":"resources","subcategory":"Compute"},{"id":"27783","title":"management_group","path":"website/docs/r/management_group.html.markdown","slug":"management_group","category":"resources","subcategory":"Management"},{"id":"27784","title":"management_lock","path":"website/docs/r/management_lock.html.markdown","slug":"management_lock","category":"resources","subcategory":"Management"},{"id":"27785","title":"maps_account","path":"website/docs/r/maps_account.html.markdown","slug":"maps_account","category":"resources","subcategory":"Maps"},{"id":"27786","title":"mariadb_configuration","path":"website/docs/r/mariadb_configuration.html.markdown","slug":"mariadb_configuration","category":"resources","subcategory":"Database"},{"id":"27787","title":"mariadb_database","path":"website/docs/r/mariadb_database.html.markdown","slug":"mariadb_database","category":"resources","subcategory":"Database"},{"id":"27788","title":"mariadb_firewall_rule","path":"website/docs/r/mariadb_firewall_rule.html.markdown","slug":"mariadb_firewall_rule","category":"resources","subcategory":"Database"},{"id":"27789","title":"mariadb_server","path":"website/docs/r/mariadb_server.html.markdown","slug":"mariadb_server","category":"resources","subcategory":"Database"},{"id":"27790","title":"mariadb_virtual_network_rule","path":"website/docs/r/mariadb_virtual_network_rule.html.markdown","slug":"mariadb_virtual_network_rule","category":"resources","subcategory":"Database"},{"id":"27791","title":"marketplace_agreement","path":"website/docs/r/marketplace_agreement.html.markdown","slug":"marketplace_agreement","category":"resources","subcategory":"Compute"},{"id":"27792","title":"media_services_account","path":"website/docs/r/media_services_account.html.markdown","slug":"media_services_account","category":"resources","subcategory":"Media"},{"id":"27793","title":"metric_alertrule","path":"website/docs/r/metric_alertrule.html.markdown","slug":"metric_alertrule","category":"resources","subcategory":"Monitor"},{"id":"27794","title":"monitor_action_group","path":"website/docs/r/monitor_action_group.html.markdown","slug":"monitor_action_group","category":"resources","subcategory":"Monitor"},{"id":"27795","title":"monitor_activity_log_alert","path":"website/docs/r/monitor_activity_log_alert.html.markdown","slug":"monitor_activity_log_alert","category":"resources","subcategory":"Monitor"},{"id":"27796","title":"monitor_autoscale_setting","path":"website/docs/r/monitor_autoscale_setting.html.markdown","slug":"monitor_autoscale_setting","category":"resources","subcategory":"Monitor"},{"id":"27797","title":"monitor_diagnostic_setting","path":"website/docs/r/monitor_diagnostic_setting.html.markdown","slug":"monitor_diagnostic_setting","category":"resources","subcategory":"Monitor"},{"id":"27798","title":"monitor_log_profile","path":"website/docs/r/monitor_log_profile.html.markdown","slug":"monitor_log_profile","category":"resources","subcategory":"Monitor"},{"id":"27799","title":"monitor_metric_alert","path":"website/docs/r/monitor_metric_alert.html.markdown","slug":"monitor_metric_alert","category":"resources","subcategory":"Monitor"},{"id":"27800","title":"monitor_metric_alertrule","path":"website/docs/r/monitor_metric_alertrule.html.markdown","slug":"monitor_metric_alertrule","category":"resources","subcategory":"Monitor"},{"id":"27801","title":"mssql_elasticpool","path":"website/docs/r/mssql_elasticpool.html.markdown","slug":"mssql_elasticpool","category":"resources","subcategory":"Database"},{"id":"27802","title":"mysql_configuration","path":"website/docs/r/mysql_configuration.html.markdown","slug":"mysql_configuration","category":"resources","subcategory":"Database"},{"id":"27803","title":"mysql_database","path":"website/docs/r/mysql_database.html.markdown","slug":"mysql_database","category":"resources","subcategory":"Database"},{"id":"27804","title":"mysql_firewall_rule","path":"website/docs/r/mysql_firewall_rule.html.markdown","slug":"mysql_firewall_rule","category":"resources","subcategory":"Database"},{"id":"27805","title":"mysql_server","path":"website/docs/r/mysql_server.html.markdown","slug":"mysql_server","category":"resources","subcategory":"Database"},{"id":"27806","title":"mysql_virtual_network_rule","path":"website/docs/r/mysql_virtual_network_rule.html.markdown","slug":"mysql_virtual_network_rule","category":"resources","subcategory":"Database"},{"id":"27807","title":"netapp_account","path":"website/docs/r/netapp_account.html.markdown","slug":"netapp_account","category":"resources","subcategory":"NetApp"},{"id":"27808","title":"netapp_pool","path":"website/docs/r/netapp_pool.html.markdown","slug":"netapp_pool","category":"resources","subcategory":"NetApp"},{"id":"27809","title":"network_connection_monitor","path":"website/docs/r/network_connection_monitor.html.markdown","slug":"network_connection_monitor","category":"resources","subcategory":"Network"},{"id":"27810","title":"network_ddos_protection_plan","path":"website/docs/r/network_ddos_protection_plan.html.markdown","slug":"network_ddos_protection_plan","category":"resources","subcategory":"Network"},{"id":"27811","title":"network_interface","path":"website/docs/r/network_interface.html.markdown","slug":"network_interface","category":"resources","subcategory":"Network"},{"id":"27812","title":"network_interface_application_gateway_backend_address_pool_association","path":"website/docs/r/network_interface_application_gateway_backend_address_pool_association.html.markdown","slug":"network_interface_application_gateway_backend_address_pool_association","category":"resources","subcategory":"Network"},{"id":"27813","title":"network_interface_application_security_group_association","path":"website/docs/r/network_interface_application_security_group_association.html.markdown","slug":"network_interface_application_security_group_association","category":"resources","subcategory":"Network"},{"id":"27814","title":"network_interface_backend_address_pool_association","path":"website/docs/r/network_interface_backend_address_pool_association.html.markdown","slug":"network_interface_backend_address_pool_association","category":"resources","subcategory":"Network"},{"id":"27815","title":"network_interface_nat_rule_association","path":"website/docs/r/network_interface_nat_rule_association.html.markdown","slug":"network_interface_nat_rule_association","category":"resources","subcategory":"Network"},{"id":"27816","title":"network_packet_capture","path":"website/docs/r/network_packet_capture.html.markdown","slug":"network_packet_capture","category":"resources","subcategory":"Network"},{"id":"27817","title":"network_profile","path":"website/docs/r/network_profile.html.markdown","slug":"network_profile","category":"resources","subcategory":"Network"},{"id":"27818","title":"network_security_group","path":"website/docs/r/network_security_group.html.markdown","slug":"network_security_group","category":"resources","subcategory":"Network"},{"id":"27819","title":"network_security_rule","path":"website/docs/r/network_security_rule.html.markdown","slug":"network_security_rule","category":"resources","subcategory":"Network"},{"id":"27820","title":"network_watcher","path":"website/docs/r/network_watcher.html.markdown","slug":"network_watcher","category":"resources","subcategory":"Network"},{"id":"27821","title":"notification_hub","path":"website/docs/r/notification_hub.html.markdown","slug":"notification_hub","category":"resources","subcategory":"Messaging"},{"id":"27822","title":"notification_hub_authorization_rule","path":"website/docs/r/notification_hub_authorization_rule.html.markdown","slug":"notification_hub_authorization_rule","category":"resources","subcategory":"Messaging"},{"id":"27823","title":"notification_hub_namespace","path":"website/docs/r/notification_hub_namespace.html.markdown","slug":"notification_hub_namespace","category":"resources","subcategory":"Messaging"},{"id":"27824","title":"packet_capture","path":"website/docs/r/packet_capture.html.markdown","slug":"packet_capture","category":"resources","subcategory":"Network"},{"id":"27825","title":"policy_assignment","path":"website/docs/r/policy_assignment.html.markdown","slug":"policy_assignment","category":"resources","subcategory":"Policy"},{"id":"27826","title":"policy_definition","path":"website/docs/r/policy_definition.html.markdown","slug":"policy_definition","category":"resources","subcategory":"Policy"},{"id":"27827","title":"policy_set_definition","path":"website/docs/r/policy_set_definition.html.markdown","slug":"policy_set_definition","category":"resources","subcategory":"Policy"},{"id":"27828","title":"postgresql_configuration","path":"website/docs/r/postgresql_configuration.html.markdown","slug":"postgresql_configuration","category":"resources","subcategory":"Database"},{"id":"27829","title":"postgresql_database","path":"website/docs/r/postgresql_database.html.markdown","slug":"postgresql_database","category":"resources","subcategory":"Database"},{"id":"27830","title":"postgresql_firewall_rule","path":"website/docs/r/postgresql_firewall_rule.html.markdown","slug":"postgresql_firewall_rule","category":"resources","subcategory":"Database"},{"id":"27831","title":"postgresql_server","path":"website/docs/r/postgresql_server.html.markdown","slug":"postgresql_server","category":"resources","subcategory":"Database"},{"id":"27832","title":"postgresql_virtual_network_rule","path":"website/docs/r/postgresql_virtual_network_rule.html.markdown","slug":"postgresql_virtual_network_rule","category":"resources","subcategory":"Database"},{"id":"27833","title":"private_dns_a_record","path":"website/docs/r/private_dns_a_record.html.markdown","slug":"private_dns_a_record","category":"resources","subcategory":"Private DNS"},{"id":"27834","title":"private_dns_aaaa_record","path":"website/docs/r/private_dns_aaaa_record.html.markdown","slug":"private_dns_aaaa_record","category":"resources","subcategory":"Private DNS"},{"id":"27835","title":"private_dns_cname_record","path":"website/docs/r/private_dns_cname_record.html.markdown","slug":"private_dns_cname_record","category":"resources","subcategory":"Private DNS"},{"id":"27836","title":"private_dns_ptr_record","path":"website/docs/r/private_dns_ptr_record.html.markdown","slug":"private_dns_ptr_record","category":"resources","subcategory":"Private DNS"},{"id":"27837","title":"private_dns_srv_record","path":"website/docs/r/private_dns_srv_record.html.markdown","slug":"private_dns_srv_record","category":"resources","subcategory":"Private DNS"},{"id":"27838","title":"private_dns_zone","path":"website/docs/r/private_dns_zone.html.markdown","slug":"private_dns_zone","category":"resources","subcategory":"Private DNS"},{"id":"27839","title":"private_dns_zone_virtual_network_link","path":"website/docs/r/private_dns_zone_virtual_network_link.html.markdown","slug":"private_dns_zone_virtual_network_link","category":"resources","subcategory":"Private DNS"},{"id":"27840","title":"private_link_service","path":"website/docs/r/private_link_service.html.markdown","slug":"private_link_service","category":"resources","subcategory":""},{"id":"27841","title":"proximity_placement_group","path":"website/docs/r/proximity_placement_group.html.markdown","slug":"proximity_placement_group","category":"resources","subcategory":"Compute"},{"id":"27842","title":"public_ip","path":"website/docs/r/public_ip.html.markdown","slug":"public_ip","category":"resources","subcategory":"Network"},{"id":"27843","title":"public_ip_prefix","path":"website/docs/r/public_ip_prefix.html.markdown","slug":"public_ip_prefix","category":"resources","subcategory":"Network"},{"id":"27844","title":"recovery_network_mapping","path":"website/docs/r/recovery_network_mapping.html.markdown","slug":"recovery_network_mapping","category":"resources","subcategory":"Recovery Services"},{"id":"27845","title":"recovery_services_fabric","path":"website/docs/r/recovery_services_fabric.html.markdown","slug":"recovery_services_fabric","category":"resources","subcategory":"Recovery Services"},{"id":"27846","title":"recovery_services_protected_vm","path":"website/docs/r/recovery_services_protected_vm.markdown","slug":"recovery_services_protected_vm","category":"resources","subcategory":"Recovery Services"},{"id":"27847","title":"recovery_services_protection_container","path":"website/docs/r/recovery_services_protection_container.html.markdown","slug":"recovery_services_protection_container","category":"resources","subcategory":"Recovery Services"},{"id":"27848","title":"recovery_services_protection_container_mapping","path":"website/docs/r/recovery_services_protection_container_mapping.html.markdown","slug":"recovery_services_protection_container_mapping","category":"resources","subcategory":"Recovery Services"},{"id":"27849","title":"recovery_services_protection_policy_vm","path":"website/docs/r/recovery_services_protection_policy_vm.markdown","slug":"recovery_services_protection_policy_vm","category":"resources","subcategory":"Recovery Services"},{"id":"27850","title":"recovery_services_replicated_vm","path":"website/docs/r/recovery_services_replicated_vm.html.markdown","slug":"recovery_services_replicated_vm","category":"resources","subcategory":"Recovery Services"},{"id":"27851","title":"recovery_services_replication_policy","path":"website/docs/r/recovery_services_replication_policy.html.markdown","slug":"recovery_services_replication_policy","category":"resources","subcategory":"Recovery Services"},{"id":"27852","title":"recovery_services_vault","path":"website/docs/r/recovery_services_vault.markdown","slug":"recovery_services_vault","category":"resources","subcategory":"Recovery Services"},{"id":"27853","title":"redis_cache","path":"website/docs/r/redis_cache.html.markdown","slug":"redis_cache","category":"resources","subcategory":"Redis"},{"id":"27854","title":"redis_firewall_rule","path":"website/docs/r/redis_firewall_rule.html.markdown","slug":"redis_firewall_rule","category":"resources","subcategory":"Redis"},{"id":"27855","title":"relay_hybrid_connection","path":"website/docs/r/relay_hybrid_connection.html.markdown","slug":"relay_hybrid_connection","category":"resources","subcategory":"Messaging"},{"id":"27856","title":"relay_namespace","path":"website/docs/r/relay_namespace.html.markdown","slug":"relay_namespace","category":"resources","subcategory":"Messaging"},{"id":"27857","title":"resource_group","path":"website/docs/r/resource_group.html.markdown","slug":"resource_group","category":"resources","subcategory":"Base"},{"id":"27858","title":"role_assignment","path":"website/docs/r/role_assignment.html.markdown","slug":"role_assignment","category":"resources","subcategory":"Authorization"},{"id":"27859","title":"role_definition","path":"website/docs/r/role_definition.html.markdown","slug":"role_definition","category":"resources","subcategory":"Authorization"},{"id":"27860","title":"route","path":"website/docs/r/route.html.markdown","slug":"route","category":"resources","subcategory":"Network"},{"id":"27861","title":"route_table","path":"website/docs/r/route_table.html.markdown","slug":"route_table","category":"resources","subcategory":"Network"},{"id":"27862","title":"scheduler_job","path":"website/docs/r/scheduler_job.html.markdown","slug":"scheduler_job","category":"resources","subcategory":"Scheduler"},{"id":"27863","title":"scheduler_job_collection","path":"website/docs/r/scheduler_job_collection.html.markdown","slug":"scheduler_job_collection","category":"resources","subcategory":"Scheduler"},{"id":"27864","title":"search_service","path":"website/docs/r/search_service.html.markdown","slug":"search_service","category":"resources","subcategory":"Search"},{"id":"27865","title":"security_center_contact","path":"website/docs/r/security_center_contact.markdown","slug":"security_center_contact","category":"resources","subcategory":"Security Center"},{"id":"27866","title":"security_center_subscription_pricing","path":"website/docs/r/security_center_subscription_pricing.markdown","slug":"security_center_subscription_pricing","category":"resources","subcategory":"Security Center"},{"id":"27867","title":"security_center_workspace","path":"website/docs/r/security_center_workspace.markdown","slug":"security_center_workspace","category":"resources","subcategory":"Security Center"},{"id":"27868","title":"service_fabric_cluster","path":"website/docs/r/service_fabric_cluster.html.markdown","slug":"service_fabric_cluster","category":"resources","subcategory":"Service Fabric"},{"id":"27869","title":"servicebus_namespace","path":"website/docs/r/servicebus_namespace.html.markdown","slug":"servicebus_namespace","category":"resources","subcategory":"Messaging"},{"id":"27870","title":"servicebus_namespace_authorization_rule","path":"website/docs/r/servicebus_namespace_authorization_rule.html.markdown","slug":"servicebus_namespace_authorization_rule","category":"resources","subcategory":"Messaging"},{"id":"27871","title":"servicebus_queue","path":"website/docs/r/servicebus_queue.html.markdown","slug":"servicebus_queue","category":"resources","subcategory":"Messaging"},{"id":"27872","title":"servicebus_queue_authorization_rule","path":"website/docs/r/servicebus_queue_authorization_rule.html.markdown","slug":"servicebus_queue_authorization_rule","category":"resources","subcategory":"Messaging"},{"id":"27873","title":"servicebus_subscription","path":"website/docs/r/servicebus_subscription.html.markdown","slug":"servicebus_subscription","category":"resources","subcategory":"Messaging"},{"id":"27874","title":"servicebus_subscription_rule","path":"website/docs/r/servicebus_subscription_rule.html.markdown","slug":"servicebus_subscription_rule","category":"resources","subcategory":"Messaging"},{"id":"27875","title":"servicebus_topic","path":"website/docs/r/servicebus_topic.html.markdown","slug":"servicebus_topic","category":"resources","subcategory":"Messaging"},{"id":"27876","title":"servicebus_topic_authorization_rule","path":"website/docs/r/servicebus_topic_authorization_rule.html.markdown","slug":"servicebus_topic_authorization_rule","category":"resources","subcategory":"Messaging"},{"id":"27877","title":"shared_image","path":"website/docs/r/shared_image.html.markdown","slug":"shared_image","category":"resources","subcategory":"Compute"},{"id":"27878","title":"shared_image_gallery","path":"website/docs/r/shared_image_gallery.html.markdown","slug":"shared_image_gallery","category":"resources","subcategory":"Compute"},{"id":"27879","title":"shared_image_version","path":"website/docs/r/shared_image_version.html.markdown","slug":"shared_image_version","category":"resources","subcategory":"Compute"},{"id":"27880","title":"signalr_service","path":"website/docs/r/signalr_service.html.markdown","slug":"signalr_service","category":"resources","subcategory":"Messaging"},{"id":"27881","title":"snapshot","path":"website/docs/r/snapshot.html.markdown","slug":"snapshot","category":"resources","subcategory":"Compute"},{"id":"27882","title":"sql_active_directory_administrator","path":"website/docs/r/sql_active_directory_administrator.markdown","slug":"sql_active_directory_administrator","category":"resources","subcategory":"Database"},{"id":"27883","title":"sql_database","path":"website/docs/r/sql_database.html.markdown","slug":"sql_database","category":"resources","subcategory":"Database"},{"id":"27884","title":"sql_elasticpool","path":"website/docs/r/sql_elasticpool.html.markdown","slug":"sql_elasticpool","category":"resources","subcategory":"Database"},{"id":"27885","title":"sql_failover_group","path":"website/docs/r/sql_failover_group.html.markdown","slug":"sql_failover_group","category":"resources","subcategory":"Database"},{"id":"27886","title":"sql_firewall_rule","path":"website/docs/r/sql_firewall_rule.html.markdown","slug":"sql_firewall_rule","category":"resources","subcategory":"Database"},{"id":"27887","title":"sql_server","path":"website/docs/r/sql_server.html.markdown","slug":"sql_server","category":"resources","subcategory":"Database"},{"id":"27888","title":"sql_virtual_network_rule","path":"website/docs/r/sql_virtual_network_rule.html.markdown","slug":"sql_virtual_network_rule","category":"resources","subcategory":"Database"},{"id":"27889","title":"storage_account","path":"website/docs/r/storage_account.html.markdown","slug":"storage_account","category":"resources","subcategory":"Storage"},{"id":"27890","title":"storage_blob","path":"website/docs/r/storage_blob.html.markdown","slug":"storage_blob","category":"resources","subcategory":"Storage"},{"id":"27891","title":"storage_container","path":"website/docs/r/storage_container.html.markdown","slug":"storage_container","category":"resources","subcategory":"Storage"},{"id":"27892","title":"storage_data_lake_gen2_filesystem","path":"website/docs/r/storage_data_lake_gen2_filesystem.html.markdown","slug":"storage_data_lake_gen2_filesystem","category":"resources","subcategory":"Storage"},{"id":"27893","title":"storage_management_policy","path":"website/docs/r/storage_management_policy.html.markdown","slug":"storage_management_policy","category":"resources","subcategory":"Storage"},{"id":"27894","title":"storage_queue","path":"website/docs/r/storage_queue.html.markdown","slug":"storage_queue","category":"resources","subcategory":"Storage"},{"id":"27895","title":"storage_share","path":"website/docs/r/storage_share.html.markdown","slug":"storage_share","category":"resources","subcategory":"Storage"},{"id":"27896","title":"storage_share_directory","path":"website/docs/r/storage_share_directory.html.markdown","slug":"storage_share_directory","category":"resources","subcategory":"Storage"},{"id":"27897","title":"storage_table","path":"website/docs/r/storage_table.html.markdown","slug":"storage_table","category":"resources","subcategory":"Storage"},{"id":"27898","title":"storage_table_entity","path":"website/docs/r/storage_table_entity.html.markdown","slug":"storage_table_entity","category":"resources","subcategory":"Storage"},{"id":"27899","title":"stream_analytics_function_javascript_udf","path":"website/docs/r/stream_analytics_function_javascript_udf.html.markdown","slug":"stream_analytics_function_javascript_udf","category":"resources","subcategory":"Stream Analytics"},{"id":"27900","title":"stream_analytics_job","path":"website/docs/r/stream_analytics_job.html.markdown","slug":"stream_analytics_job","category":"resources","subcategory":"Stream Analytics"},{"id":"27901","title":"stream_analytics_output_blob","path":"website/docs/r/stream_analytics_output_blob.html.markdown","slug":"stream_analytics_output_blob","category":"resources","subcategory":"Stream Analytics"},{"id":"27902","title":"stream_analytics_output_eventhub","path":"website/docs/r/stream_analytics_output_eventhub.html.markdown","slug":"stream_analytics_output_eventhub","category":"resources","subcategory":"Stream Analytics"},{"id":"27903","title":"stream_analytics_output_mssql","path":"website/docs/r/stream_analytics_output_mssql.html.markdown","slug":"stream_analytics_output_mssql","category":"resources","subcategory":"Stream Analytics"},{"id":"27904","title":"stream_analytics_output_servicebus_queue","path":"website/docs/r/stream_analytics_output_servicebus_queue.html.markdown","slug":"stream_analytics_output_servicebus_queue","category":"resources","subcategory":"Stream Analytics"},{"id":"27905","title":"stream_analytics_output_servicebus_topic","path":"website/docs/r/stream_analytics_output_servicebus_topic.html.markdown","slug":"stream_analytics_output_servicebus_topic","category":"resources","subcategory":"Stream Analytics"},{"id":"27906","title":"stream_analytics_stream_input_blob","path":"website/docs/r/stream_analytics_stream_input_blob.html.markdown","slug":"stream_analytics_stream_input_blob","category":"resources","subcategory":"Stream Analytics"},{"id":"27907","title":"stream_analytics_stream_input_eventhub","path":"website/docs/r/stream_analytics_stream_input_eventhub.html.markdown","slug":"stream_analytics_stream_input_eventhub","category":"resources","subcategory":"Stream Analytics"},{"id":"27908","title":"stream_analytics_stream_input_iothub","path":"website/docs/r/stream_analytics_stream_input_iothub.html.markdown","slug":"stream_analytics_stream_input_iothub","category":"resources","subcategory":"Stream Analytics"},{"id":"27909","title":"subnet","path":"website/docs/r/subnet.html.markdown","slug":"subnet","category":"resources","subcategory":"Network"},{"id":"27910","title":"subnet_network_security_group_association","path":"website/docs/r/subnet_network_security_group_association.html.markdown","slug":"subnet_network_security_group_association","category":"resources","subcategory":"Network"},{"id":"27911","title":"subnet_route_table_association","path":"website/docs/r/subnet_route_table_association.html.markdown","slug":"subnet_route_table_association","category":"resources","subcategory":"Network"},{"id":"27912","title":"template_deployment","path":"website/docs/r/template_deployment.html.markdown","slug":"template_deployment","category":"resources","subcategory":"Template"},{"id":"27913","title":"traffic_manager_endpoint","path":"website/docs/r/traffic_manager_endpoint.html.markdown","slug":"traffic_manager_endpoint","category":"resources","subcategory":"Network"},{"id":"27914","title":"traffic_manager_profile","path":"website/docs/r/traffic_manager_profile.html.markdown","slug":"traffic_manager_profile","category":"resources","subcategory":"Network"},{"id":"27915","title":"user_assigned_identity","path":"website/docs/r/user_assigned_identity.markdown","slug":"user_assigned_identity","category":"resources","subcategory":"Authorization"},{"id":"27916","title":"virtual_machine","path":"website/docs/r/virtual_machine.html.markdown","slug":"virtual_machine","category":"resources","subcategory":"Compute"},{"id":"27917","title":"virtual_machine_data_disk_attachment","path":"website/docs/r/virtual_machine_data_disk_attachment.html.markdown","slug":"virtual_machine_data_disk_attachment","category":"resources","subcategory":"Compute"},{"id":"27918","title":"virtual_machine_extension","path":"website/docs/r/virtual_machine_extension.html.markdown","slug":"virtual_machine_extension","category":"resources","subcategory":"Compute"},{"id":"27919","title":"virtual_machine_scale_set","path":"website/docs/r/virtual_machine_scale_set.html.markdown","slug":"virtual_machine_scale_set","category":"resources","subcategory":"Compute"},{"id":"27920","title":"virtual_network","path":"website/docs/r/virtual_network.html.markdown","slug":"virtual_network","category":"resources","subcategory":"Network"},{"id":"27921","title":"virtual_network_gateway","path":"website/docs/r/virtual_network_gateway.html.markdown","slug":"virtual_network_gateway","category":"resources","subcategory":"Network"},{"id":"27922","title":"virtual_network_gateway_connection","path":"website/docs/r/virtual_network_gateway_connection.html.markdown","slug":"virtual_network_gateway_connection","category":"resources","subcategory":"Network"},{"id":"27923","title":"virtual_network_peering","path":"website/docs/r/virtual_network_peering.html.markdown","slug":"virtual_network_peering","category":"resources","subcategory":"Network"},{"id":"27924","title":"virtual_wan","path":"website/docs/r/virtual_wan.html.markdown","slug":"virtual_wan","category":"resources","subcategory":"Network"},{"id":"27925","title":"web_application_firewall_policy","path":"website/docs/r/web_application_firewall_policy.html.markdown","slug":"web_application_firewall_policy","category":"resources","subcategory":"Network"},{"id":"27926","title":"windows_virtual_machine_scale_set","path":"website/docs/r/windows_virtual_machine_scale_set.html.markdown","slug":"windows_virtual_machine_scale_set","category":"resources","subcategory":"Beta"}]} +{ + "id": "hashicorp/azurerm/2.53.0", + "owner": "hashicorp", + "namespace": "hashicorp", + "name": "azurerm", + "alias": "azurerm", + "version": "2.53.0", + "tag": "2.53.0", + "description": "terraform-provider-azurerm", + "source": "https://github.com/terraform-providers/terraform-provider-azurerm", + "published_at": "2019-11-26T08:22:56Z", + "downloads": 5181001, + "official": true, + "versions": [ + "2.52.0", + "2.53.0" + ], + "docs": [ + { + "id": "27489", + "title": "overview", + "path": "website/docs/index.html.markdown", + "slug": "index", + "category": "overview", + "subcategory": "" + }, + { + "id": "27490", + "title": "api_management", + "path": "website/docs/d/api_management.html.markdown", + "slug": "api_management", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27491", + "title": "api_management_api", + "path": "website/docs/d/api_management_api.html.markdown", + "slug": "api_management_api", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27492", + "title": "api_management_group", + "path": "website/docs/d/api_management_group.html.markdown", + "slug": "api_management_group", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27493", + "title": "api_management_product", + "path": "website/docs/d/api_management_product.html.markdown", + "slug": "api_management_product", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27494", + "title": "api_management_user", + "path": "website/docs/d/api_management_user.html.markdown", + "slug": "api_management_user", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27495", + "title": "app_service", + "path": "website/docs/d/app_service.html.markdown", + "slug": "app_service", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27496", + "title": "app_service_certificate", + "path": "website/docs/d/app_service_certificate.html.markdown", + "slug": "app_service_certificate", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27497", + "title": "app_service_certificate_order", + "path": "website/docs/d/app_service_certificate_order.html.markdown", + "slug": "app_service_certificate_order", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27498", + "title": "app_service_plan", + "path": "website/docs/d/app_service_plan.html.markdown", + "slug": "app_service_plan", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27499", + "title": "application_insights", + "path": "website/docs/d/application_insights.html.markdown", + "slug": "application_insights", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27500", + "title": "application_security_group", + "path": "website/docs/d/application_security_group.html.markdown", + "slug": "application_security_group", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27501", + "title": "automation_account", + "path": "website/docs/d/automation_account.html.markdown", + "slug": "automation_account", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27502", + "title": "automation_variable_bool", + "path": "website/docs/d/automation_variable_bool.html.markdown", + "slug": "automation_variable_bool", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27503", + "title": "automation_variable_datetime", + "path": "website/docs/d/automation_variable_datetime.html.markdown", + "slug": "automation_variable_datetime", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27504", + "title": "automation_variable_int", + "path": "website/docs/d/automation_variable_int.html.markdown", + "slug": "automation_variable_int", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27505", + "title": "automation_variable_string", + "path": "website/docs/d/automation_variable_string.html.markdown", + "slug": "automation_variable_string", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27506", + "title": "availability_set", + "path": "website/docs/d/availability_set.html.markdown", + "slug": "availability_set", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27507", + "title": "azuread_application", + "path": "website/docs/d/azuread_application.html.markdown", + "slug": "azuread_application", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27508", + "title": "azuread_service_principal", + "path": "website/docs/d/azuread_service_principal.html.markdown", + "slug": "azuread_service_principal", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27509", + "title": "batch_account", + "path": "website/docs/d/batch_account.html.markdown", + "slug": "batch_account", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27510", + "title": "batch_certificate", + "path": "website/docs/d/batch_certificate.html.markdown", + "slug": "batch_certificate", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27511", + "title": "batch_pool", + "path": "website/docs/d/batch_pool.html.markdown", + "slug": "batch_pool", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27512", + "title": "builtin_role_definition", + "path": "website/docs/d/builtin_role_definition.markdown", + "slug": "builtin_role_definition", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27513", + "title": "cdn_profile", + "path": "website/docs/d/cdn_profile.html.markdown", + "slug": "cdn_profile", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27514", + "title": "client_config", + "path": "website/docs/d/client_config.html.markdown", + "slug": "client_config", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27515", + "title": "container_registry", + "path": "website/docs/d/container_registry.markdown", + "slug": "container_registry", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27516", + "title": "cosmosdb_account", + "path": "website/docs/d/cosmosdb_account.html.markdown", + "slug": "cosmosdb_account", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27517", + "title": "data_factory", + "path": "website/docs/d/data_factory.html.markdown", + "slug": "data_factory", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27518", + "title": "data_lake_store", + "path": "website/docs/d/data_lake_store.html.markdown", + "slug": "data_lake_store", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27519", + "title": "dev_test_lab", + "path": "website/docs/d/dev_test_lab.html.markdown", + "slug": "dev_test_lab", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27520", + "title": "dev_test_virtual_network", + "path": "website/docs/d/dev_test_virtual_network.html.markdown", + "slug": "dev_test_virtual_network", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27521", + "title": "dns_zone", + "path": "website/docs/d/dns_zone.html.markdown", + "slug": "dns_zone", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27522", + "title": "eventhub_namespace", + "path": "website/docs/d/eventhub_namespace.html.markdown", + "slug": "eventhub_namespace", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27523", + "title": "express_route_circuit", + "path": "website/docs/d/express_route_circuit.html.markdown", + "slug": "express_route_circuit", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27524", + "title": "firewall", + "path": "website/docs/d/firewall.html.markdown", + "slug": "firewall", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27525", + "title": "hdinsight_cluster", + "path": "website/docs/d/hdinsight_cluster.html.markdown", + "slug": "hdinsight_cluster", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27526", + "title": "healthcare_service", + "path": "website/docs/d/healthcare_service.html.markdown", + "slug": "healthcare_service", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27527", + "title": "image", + "path": "website/docs/d/image.html.markdown", + "slug": "image", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27528", + "title": "key_vault", + "path": "website/docs/d/key_vault.html.markdown", + "slug": "key_vault", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27529", + "title": "key_vault_access_policy", + "path": "website/docs/d/key_vault_access_policy.html.markdown", + "slug": "key_vault_access_policy", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27530", + "title": "key_vault_key", + "path": "website/docs/d/key_vault_key.html.markdown", + "slug": "key_vault_key", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27531", + "title": "key_vault_secret", + "path": "website/docs/d/key_vault_secret.html.markdown", + "slug": "key_vault_secret", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27532", + "title": "kubernetes_cluster", + "path": "website/docs/d/kubernetes_cluster.html.markdown", + "slug": "kubernetes_cluster", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27533", + "title": "kubernetes_service_versions", + "path": "website/docs/d/kubernetes_service_versions.html.markdown", + "slug": "kubernetes_service_versions", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27534", + "title": "loadbalancer", + "path": "website/docs/d/loadbalancer.html.markdown", + "slug": "loadbalancer", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27535", + "title": "loadbalancer_backend_address_pool", + "path": "website/docs/d/loadbalancer_backend_address_pool.html.markdown", + "slug": "loadbalancer_backend_address_pool", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27536", + "title": "log_analytics_workspace", + "path": "website/docs/d/log_analytics_workspace.html.markdown", + "slug": "log_analytics_workspace", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27537", + "title": "logic_app_workflow", + "path": "website/docs/d/logic_app_workflow.html.markdown", + "slug": "logic_app_workflow", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27538", + "title": "managed_disk", + "path": "website/docs/d/managed_disk.html.markdown", + "slug": "managed_disk", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27539", + "title": "management_group", + "path": "website/docs/d/management_group.html.markdown", + "slug": "management_group", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27540", + "title": "maps_account", + "path": "website/docs/d/maps_account.html.markdown", + "slug": "maps_account", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27541", + "title": "monitor_action_group", + "path": "website/docs/d/monitor_action_group.html.markdown", + "slug": "monitor_action_group", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27542", + "title": "monitor_diagnostic_categories", + "path": "website/docs/d/monitor_diagnostic_categories.html.markdown", + "slug": "monitor_diagnostic_categories", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27543", + "title": "monitor_log_profile", + "path": "website/docs/d/monitor_log_profile.html.markdown", + "slug": "monitor_log_profile", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27544", + "title": "mssql_elasticpool", + "path": "website/docs/d/mssql_elasticpool.html.markdown", + "slug": "mssql_elasticpool", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27545", + "title": "netapp_account", + "path": "website/docs/d/netapp_account.html.markdown", + "slug": "netapp_account", + "category": "data-sources", + "subcategory": "NetApp" + }, + { + "id": "27546", + "title": "netapp_pool", + "path": "website/docs/d/netapp_pool.html.markdown", + "slug": "netapp_pool", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27547", + "title": "network_ddos_protection_plan", + "path": "website/docs/d/network_ddos_protection_plan.html.markdown", + "slug": "network_ddos_protection_plan", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27548", + "title": "network_interface", + "path": "website/docs/d/network_interface.html.markdown", + "slug": "network_interface", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27549", + "title": "network_security_group", + "path": "website/docs/d/network_security_group.html.markdown", + "slug": "network_security_group", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27550", + "title": "network_watcher", + "path": "website/docs/d/network_watcher.html.markdown", + "slug": "network_watcher", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27551", + "title": "notification_hub", + "path": "website/docs/d/notification_hub.html.markdown", + "slug": "notification_hub", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27552", + "title": "notification_hub_namespace", + "path": "website/docs/d/notification_hub_namespace.html.markdown", + "slug": "notification_hub_namespace", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27553", + "title": "platform_image", + "path": "website/docs/d/platform_image.html.markdown", + "slug": "platform_image", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27554", + "title": "policy_definition", + "path": "website/docs/d/policy_definition.markdown", + "slug": "policy_definition", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27555", + "title": "postgresql_server", + "path": "website/docs/d/postgresql_server.html.markdown", + "slug": "postgresql_server", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27556", + "title": "private_link_service", + "path": "website/docs/d/private_link_service.html.markdown", + "slug": "private_link_service", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27557", + "title": "private_link_service_endpoint_connections", + "path": "website/docs/d/private_link_service_endpoint_connections.html.markdown", + "slug": "private_link_service_endpoint_connections", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27558", + "title": "proximity_placement_group", + "path": "website/docs/d/proximity_placement_group.html.markdown", + "slug": "proximity_placement_group", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27559", + "title": "public_ip", + "path": "website/docs/d/public_ip.html.markdown", + "slug": "public_ip", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27560", + "title": "public_ip_prefix", + "path": "website/docs/d/public_ip_prefix.html.markdown", + "slug": "public_ip_prefix", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27561", + "title": "public_ips", + "path": "website/docs/d/public_ips.html.markdown", + "slug": "public_ips", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27562", + "title": "recovery_services_protection_policy_vm", + "path": "website/docs/d/recovery_services_protection_policy_vm.markdown", + "slug": "recovery_services_protection_policy_vm", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27563", + "title": "recovery_services_vault", + "path": "website/docs/d/recovery_services_vault.markdown", + "slug": "recovery_services_vault", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27564", + "title": "redis_cache", + "path": "website/docs/d/redis_cache.html.markdown", + "slug": "redis_cache", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27565", + "title": "resource_group", + "path": "website/docs/d/resource_group.html.markdown", + "slug": "resource_group", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27566", + "title": "resources", + "path": "website/docs/d/resources.html.markdown", + "slug": "resources", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27567", + "title": "role_definition", + "path": "website/docs/d/role_definition.markdown", + "slug": "role_definition", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27568", + "title": "route_table", + "path": "website/docs/d/route_table.html.markdown", + "slug": "route_table", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27569", + "title": "scheduler_job_collection", + "path": "website/docs/d/scheduler_job_collection.html.markdown", + "slug": "scheduler_job_collection", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27570", + "title": "servicebus_namespace", + "path": "website/docs/d/servicebus_namespace.html.markdown", + "slug": "servicebus_namespace", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27571", + "title": "servicebus_namespace_authorization_rule", + "path": "website/docs/d/servicebus_namespace_authorization_rule.html.markdown", + "slug": "servicebus_namespace_authorization_rule", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27572", + "title": "shared_image", + "path": "website/docs/d/shared_image.html.markdown", + "slug": "shared_image", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27573", + "title": "shared_image_gallery", + "path": "website/docs/d/shared_image_gallery.html.markdown", + "slug": "shared_image_gallery", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27574", + "title": "shared_image_version", + "path": "website/docs/d/shared_image_version.html.markdown", + "slug": "shared_image_version", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27575", + "title": "snapshot", + "path": "website/docs/d/snapshot.html.markdown", + "slug": "snapshot", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27576", + "title": "sql_database", + "path": "website/docs/d/sql_database.html.markdown", + "slug": "sql_database", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27577", + "title": "sql_server", + "path": "website/docs/d/sql_server.html.markdown", + "slug": "sql_server", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27578", + "title": "storage_account", + "path": "website/docs/d/storage_account.html.markdown", + "slug": "storage_account", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27579", + "title": "storage_account_blob_container_sas", + "path": "website/docs/d/storage_account_blob_container_sas.html.markdown", + "slug": "storage_account_blob_container_sas", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27580", + "title": "storage_account_sas", + "path": "website/docs/d/storage_account_sas.html.markdown", + "slug": "storage_account_sas", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27581", + "title": "storage_management_policy", + "path": "website/docs/d/storage_management_policy.html.markdown", + "slug": "storage_management_policy", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27582", + "title": "stream_analytics_job", + "path": "website/docs/d/stream_analytics_job.html.markdown", + "slug": "stream_analytics_job", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27583", + "title": "subnet", + "path": "website/docs/d/subnet.html.markdown", + "slug": "subnet", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27584", + "title": "subscription", + "path": "website/docs/d/subscription.html.markdown", + "slug": "subscription", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27585", + "title": "subscriptions", + "path": "website/docs/d/subscriptions.html.markdown", + "slug": "subscriptions", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27586", + "title": "traffic_manager_geographical_location", + "path": "website/docs/d/traffic_manager_geographical_location.html.markdown", + "slug": "traffic_manager_geographical_location", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27587", + "title": "user_assigned_identity", + "path": "website/docs/d/user_assigned_identity.html.markdown", + "slug": "user_assigned_identity", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27588", + "title": "virtual_hub", + "path": "website/docs/d/virtual_hub.html.markdown", + "slug": "virtual_hub", + "category": "data-sources", + "subcategory": "Network" + }, + { + "id": "27589", + "title": "virtual_machine", + "path": "website/docs/d/virtual_machine.html.markdown", + "slug": "virtual_machine", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27590", + "title": "virtual_network", + "path": "website/docs/d/virtual_network.html.markdown", + "slug": "virtual_network", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27591", + "title": "virtual_network_gateway", + "path": "website/docs/d/virtual_network_gateway.html.markdown", + "slug": "virtual_network_gateway", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27592", + "title": "virtual_network_gateway_connection", + "path": "website/docs/d/virtual_network_gateway_connection.html.markdown", + "slug": "virtual_network_gateway_connection", + "category": "data-sources", + "subcategory": "" + }, + { + "id": "27593", + "title": "Azure Resource Manager: 2.0 Upgrade Guide", + "path": "website/docs/guides/2.0-upgrade-guide.html.markdown", + "slug": "2.0-upgrade-guide", + "category": "guides", + "subcategory": "" + }, + { + "id": "27594", + "title": "Azure Provider: Authenticating via the Azure CLI", + "path": "website/docs/guides/azure_cli.html.markdown", + "slug": "azure_cli", + "category": "guides", + "subcategory": "" + }, + { + "id": "27595", + "title": "Azure Provider: Authenticating via Managed Identity", + "path": "website/docs/guides/managed_service_identity.html.markdown", + "slug": "managed_service_identity", + "category": "guides", + "subcategory": "" + }, + { + "id": "27596", + "title": "Azure Provider: Migrating to a renamed resource", + "path": "website/docs/guides/migrating-between-renamed-resources.html.markdown", + "slug": "migrating-between-renamed-resources", + "category": "guides", + "subcategory": "" + }, + { + "id": "27597", + "title": "Azure Active Directory: Migrating to the AzureAD Provider", + "path": "website/docs/guides/migrating-to-azuread.html.markdown", + "slug": "migrating-to-azuread", + "category": "guides", + "subcategory": "" + }, + { + "id": "27598", + "title": "Azure Provider: Authenticating via a Service Principal and a Client Certificate", + "path": "website/docs/guides/service_principal_client_certificate.html.markdown", + "slug": "service_principal_client_certificate", + "category": "guides", + "subcategory": "" + }, + { + "id": "27599", + "title": "Azure Provider: Authenticating via a Service Principal and a Client Secret", + "path": "website/docs/guides/service_principal_client_secret.html.markdown", + "slug": "service_principal_client_secret", + "category": "guides", + "subcategory": "" + }, + { + "id": "27600", + "title": "analysis_services_server", + "path": "website/docs/r/analysis_services_server.html.markdown", + "slug": "analysis_services_server", + "category": "resources", + "subcategory": "Analysis Services" + }, + { + "id": "27601", + "title": "api_management", + "path": "website/docs/r/api_management.html.markdown", + "slug": "api_management", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27602", + "title": "api_management_api", + "path": "website/docs/r/api_management_api.html.markdown", + "slug": "api_management_api", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27603", + "title": "api_management_api_operation", + "path": "website/docs/r/api_management_api_operation.html.markdown", + "slug": "api_management_api_operation", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27604", + "title": "api_management_api_operation_policy", + "path": "website/docs/r/api_management_api_operation_policy.html.markdown", + "slug": "api_management_api_operation_policy", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27605", + "title": "api_management_api_policy", + "path": "website/docs/r/api_management_api_policy.html.markdown", + "slug": "api_management_api_policy", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27606", + "title": "api_management_api_schema", + "path": "website/docs/r/api_management_api_schema.html.markdown", + "slug": "api_management_api_schema", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27607", + "title": "api_management_api_version_set", + "path": "website/docs/r/api_management_api_version_set.html.markdown", + "slug": "api_management_api_version_set", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27608", + "title": "api_management_authorization_server", + "path": "website/docs/r/api_management_authorization_server.html.markdown", + "slug": "api_management_authorization_server", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27609", + "title": "api_management_backend", + "path": "website/docs/r/api_management_backend.html.markdown", + "slug": "api_management_backend", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27610", + "title": "api_management_certificate", + "path": "website/docs/r/api_management_certificate.html.markdown", + "slug": "api_management_certificate", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27611", + "title": "api_management_group", + "path": "website/docs/r/api_management_group.html.markdown", + "slug": "api_management_group", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27612", + "title": "api_management_group_user", + "path": "website/docs/r/api_management_group_user.html.markdown", + "slug": "api_management_group_user", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27613", + "title": "api_management_logger", + "path": "website/docs/r/api_management_logger.html.markdown", + "slug": "api_management_logger", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27614", + "title": "api_management_openid_connect_provider", + "path": "website/docs/r/api_management_openid_connect_provider.html.markdown", + "slug": "api_management_openid_connect_provider", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27615", + "title": "api_management_product", + "path": "website/docs/r/api_management_product.html.markdown", + "slug": "api_management_product", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27616", + "title": "api_management_product_api", + "path": "website/docs/r/api_management_product_api.html.markdown", + "slug": "api_management_product_api", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27617", + "title": "api_management_product_group", + "path": "website/docs/r/api_management_product_group.html.markdown", + "slug": "api_management_product_group", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27618", + "title": "api_management_product_policy", + "path": "website/docs/r/api_management_product_policy.html.markdown", + "slug": "api_management_product_policy", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27619", + "title": "api_management_property", + "path": "website/docs/r/api_management_property.html.markdown", + "slug": "api_management_property", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27620", + "title": "api_management_subscription", + "path": "website/docs/r/api_management_subscription.html.markdown", + "slug": "api_management_subscription", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27621", + "title": "api_management_user", + "path": "website/docs/r/api_management_user.html.markdown", + "slug": "api_management_user", + "category": "resources", + "subcategory": "API Management" + }, + { + "id": "27622", + "title": "app_service", + "path": "website/docs/r/app_service.html.markdown", + "slug": "app_service", + "category": "resources", + "subcategory": "App Service (Web Apps)" + }, + { + "id": "27623", + "title": "app_service_active_slot", + "path": "website/docs/r/app_service_active_slot.html.markdown", + "slug": "app_service_active_slot", + "category": "resources", + "subcategory": "App Service (Web Apps)" + }, + { + "id": "27624", + "title": "app_service_certificate", + "path": "website/docs/r/app_service_certificate.html.markdown", + "slug": "app_service_certificate", + "category": "resources", + "subcategory": "App Service (Web Apps)" + }, + { + "id": "27625", + "title": "app_service_certificate_order", + "path": "website/docs/r/app_service_certificate_order.html.markdown", + "slug": "app_service_certificate_order", + "category": "resources", + "subcategory": "App Service (Web Apps)" + }, + { + "id": "27626", + "title": "app_service_custom_hostname_binding", + "path": "website/docs/r/app_service_custom_hostname_binding.html.markdown", + "slug": "app_service_custom_hostname_binding", + "category": "resources", + "subcategory": "App Service (Web Apps)" + }, + { + "id": "27627", + "title": "app_service_plan", + "path": "website/docs/r/app_service_plan.html.markdown", + "slug": "app_service_plan", + "category": "resources", + "subcategory": "App Service (Web Apps)" + }, + { + "id": "27628", + "title": "app_service_slot", + "path": "website/docs/r/app_service_slot.html.markdown", + "slug": "app_service_slot", + "category": "resources", + "subcategory": "App Service (Web Apps)" + }, + { + "id": "27629", + "title": "app_service_source_control_token", + "path": "website/docs/r/app_service_source_control_token.html.markdown", + "slug": "app_service_source_control_token", + "category": "resources", + "subcategory": "App Service (Web Apps)" + }, + { + "id": "27630", + "title": "application_gateway", + "path": "website/docs/r/application_gateway.html.markdown", + "slug": "application_gateway", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27631", + "title": "application_insights", + "path": "website/docs/r/application_insights.html.markdown", + "slug": "application_insights", + "category": "resources", + "subcategory": "Application Insights" + }, + { + "id": "27632", + "title": "application_insights_analytics_item", + "path": "website/docs/r/application_insights_analytics_item.html.markdown", + "slug": "application_insights_analytics_item", + "category": "resources", + "subcategory": "Application Insights" + }, + { + "id": "27633", + "title": "application_insights_api_key", + "path": "website/docs/r/application_insights_api_key.html.markdown", + "slug": "application_insights_api_key", + "category": "resources", + "subcategory": "Application Insights" + }, + { + "id": "27634", + "title": "application_insights_web_test", + "path": "website/docs/r/application_insights_web_test.html.markdown", + "slug": "application_insights_web_test", + "category": "resources", + "subcategory": "Application Insights" + }, + { + "id": "27635", + "title": "application_security_group", + "path": "website/docs/r/application_security_group.html.markdown", + "slug": "application_security_group", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27636", + "title": "automation_account", + "path": "website/docs/r/automation_account.html.markdown", + "slug": "automation_account", + "category": "resources", + "subcategory": "Automation" + }, + { + "id": "27637", + "title": "automation_credential", + "path": "website/docs/r/automation_credential.html.markdown", + "slug": "automation_credential", + "category": "resources", + "subcategory": "Automation" + }, + { + "id": "27638", + "title": "automation_dsc_configuration", + "path": "website/docs/r/automation_dsc_configuration.html.markdown", + "slug": "automation_dsc_configuration", + "category": "resources", + "subcategory": "Automation" + }, + { + "id": "27639", + "title": "automation_dsc_nodeconfiguration", + "path": "website/docs/r/automation_dsc_nodeconfiguration.html.markdown", + "slug": "automation_dsc_nodeconfiguration", + "category": "resources", + "subcategory": "Automation" + }, + { + "id": "27640", + "title": "automation_job_schedule", + "path": "website/docs/r/automation_job_schedule.html.markdown", + "slug": "automation_job_schedule", + "category": "resources", + "subcategory": "Automation" + }, + { + "id": "27641", + "title": "automation_module", + "path": "website/docs/r/automation_module.html.markdown", + "slug": "automation_module", + "category": "resources", + "subcategory": "Automation" + }, + { + "id": "27642", + "title": "automation_runbook", + "path": "website/docs/r/automation_runbook.html.markdown", + "slug": "automation_runbook", + "category": "resources", + "subcategory": "Automation" + }, + { + "id": "27643", + "title": "automation_schedule", + "path": "website/docs/r/automation_schedule.html.markdown", + "slug": "automation_schedule", + "category": "resources", + "subcategory": "Automation" + }, + { + "id": "27644", + "title": "automation_variable_bool", + "path": "website/docs/r/automation_variable_bool.html.markdown", + "slug": "automation_variable_bool", + "category": "resources", + "subcategory": "Automation" + }, + { + "id": "27645", + "title": "automation_variable_datetime", + "path": "website/docs/r/automation_variable_datetime.html.markdown", + "slug": "automation_variable_datetime", + "category": "resources", + "subcategory": "Automation" + }, + { + "id": "27646", + "title": "automation_variable_int", + "path": "website/docs/r/automation_variable_int.html.markdown", + "slug": "automation_variable_int", + "category": "resources", + "subcategory": "Automation" + }, + { + "id": "27647", + "title": "automation_variable_string", + "path": "website/docs/r/automation_variable_string.html.markdown", + "slug": "automation_variable_string", + "category": "resources", + "subcategory": "Automation" + }, + { + "id": "27648", + "title": "autoscale_setting", + "path": "website/docs/r/autoscale_setting.html.markdown", + "slug": "autoscale_setting", + "category": "resources", + "subcategory": "Monitor" + }, + { + "id": "27649", + "title": "availability_set", + "path": "website/docs/r/availability_set.html.markdown", + "slug": "availability_set", + "category": "resources", + "subcategory": "Compute" + }, + { + "id": "27650", + "title": "azuread_application", + "path": "website/docs/r/azuread_application.html.markdown", + "slug": "azuread_application", + "category": "resources", + "subcategory": "Azure Active Directory" + }, + { + "id": "27651", + "title": "azuread_service_principal", + "path": "website/docs/r/azuread_service_principal.html.markdown", + "slug": "azuread_service_principal", + "category": "resources", + "subcategory": "Azure Active Directory" + }, + { + "id": "27652", + "title": "azuread_service_principal_password", + "path": "website/docs/r/azuread_service_principal_password.html.markdown", + "slug": "azuread_service_principal_password", + "category": "resources", + "subcategory": "Azure Active Directory" + }, + { + "id": "27653", + "title": "bastion_host", + "path": "website/docs/r/bastion_host.html.markdown", + "slug": "bastion_host", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27654", + "title": "batch_account", + "path": "website/docs/r/batch_account.html.markdown", + "slug": "batch_account", + "category": "resources", + "subcategory": "Batch" + }, + { + "id": "27655", + "title": "batch_application", + "path": "website/docs/r/batch_application.html.markdown", + "slug": "batch_application", + "category": "resources", + "subcategory": "Batch" + }, + { + "id": "27656", + "title": "batch_certificate", + "path": "website/docs/r/batch_certificate.html.markdown", + "slug": "batch_certificate", + "category": "resources", + "subcategory": "Batch" + }, + { + "id": "27657", + "title": "batch_pool", + "path": "website/docs/r/batch_pool.html.markdown", + "slug": "batch_pool", + "category": "resources", + "subcategory": "Batch" + }, + { + "id": "27658", + "title": "bot_channel_email", + "path": "website/docs/r/bot_channel_email.markdown", + "slug": "bot_channel_email", + "category": "resources", + "subcategory": "Bot" + }, + { + "id": "27659", + "title": "bot_channel_slack", + "path": "website/docs/r/bot_channel_slack.markdown", + "slug": "bot_channel_slack", + "category": "resources", + "subcategory": "Bot" + }, + { + "id": "27660", + "title": "bot_channels_registration", + "path": "website/docs/r/bot_channels_registration.markdown", + "slug": "bot_channels_registration", + "category": "resources", + "subcategory": "Bot" + }, + { + "id": "27661", + "title": "bot_connection", + "path": "website/docs/r/bot_connection.markdown", + "slug": "bot_connection", + "category": "resources", + "subcategory": "Bot" + }, + { + "id": "27662", + "title": "bot_web_app", + "path": "website/docs/r/bot_web_app.markdown", + "slug": "bot_web_app", + "category": "resources", + "subcategory": "Bot" + }, + { + "id": "27663", + "title": "cdn_endpoint", + "path": "website/docs/r/cdn_endpoint.html.markdown", + "slug": "cdn_endpoint", + "category": "resources", + "subcategory": "CDN" + }, + { + "id": "27664", + "title": "cdn_profile", + "path": "website/docs/r/cdn_profile.html.markdown", + "slug": "cdn_profile", + "category": "resources", + "subcategory": "CDN" + }, + { + "id": "27665", + "title": "cognitive_account", + "path": "website/docs/r/cognitive_account.html.markdown", + "slug": "cognitive_account", + "category": "resources", + "subcategory": "Cognitive Services" + }, + { + "id": "27666", + "title": "connection_monitor", + "path": "website/docs/r/connection_monitor.html.markdown", + "slug": "connection_monitor", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27667", + "title": "container_group", + "path": "website/docs/r/container_group.html.markdown", + "slug": "container_group", + "category": "resources", + "subcategory": "Container" + }, + { + "id": "27668", + "title": "container_registry", + "path": "website/docs/r/container_registry.html.markdown", + "slug": "container_registry", + "category": "resources", + "subcategory": "Container" + }, + { + "id": "27669", + "title": "container_registry_webhook", + "path": "website/docs/r/container_registry_webhook.html.markdown", + "slug": "container_registry_webhook", + "category": "resources", + "subcategory": "Container" + }, + { + "id": "27670", + "title": "container_service", + "path": "website/docs/r/container_service.html.markdown", + "slug": "container_service", + "category": "resources", + "subcategory": "Container" + }, + { + "id": "27671", + "title": "cosmosdb_account", + "path": "website/docs/r/cosmosdb_account.html.markdown", + "slug": "cosmosdb_account", + "category": "resources", + "subcategory": "CosmosDB (DocumentDB)" + }, + { + "id": "27672", + "title": "cosmosdb_cassandra_keyspace", + "path": "website/docs/r/cosmosdb_cassandra_keyspace.html.markdown", + "slug": "cosmosdb_cassandra_keyspace", + "category": "resources", + "subcategory": "CosmosDB (DocumentDB)" + }, + { + "id": "27673", + "title": "cosmosdb_mongo_collection", + "path": "website/docs/r/cosmosdb_mongo_collection.html.markdown", + "slug": "cosmosdb_mongo_collection", + "category": "resources", + "subcategory": "CosmosDB (DocumentDB)" + }, + { + "id": "27674", + "title": "cosmosdb_mongo_database", + "path": "website/docs/r/cosmosdb_mongo_database.html.markdown", + "slug": "cosmosdb_mongo_database", + "category": "resources", + "subcategory": "CosmosDB (DocumentDB)" + }, + { + "id": "27675", + "title": "cosmosdb_sql_container", + "path": "website/docs/r/cosmosdb_sql_container.html.markdown", + "slug": "cosmosdb_sql_container", + "category": "resources", + "subcategory": "CosmosDB (DocumentDB)" + }, + { + "id": "27676", + "title": "cosmosdb_sql_database", + "path": "website/docs/r/cosmosdb_sql_database.html.markdown", + "slug": "cosmosdb_sql_database", + "category": "resources", + "subcategory": "CosmosDB (DocumentDB)" + }, + { + "id": "27677", + "title": "cosmosdb_table", + "path": "website/docs/r/cosmosdb_table.html.markdown", + "slug": "cosmosdb_table", + "category": "resources", + "subcategory": "CosmosDB (DocumentDB)" + }, + { + "id": "27678", + "title": "dashboard", + "path": "website/docs/r/dashboard.html.markdown", + "slug": "dashboard", + "category": "resources", + "subcategory": "Portal" + }, + { + "id": "27679", + "title": "data_factory", + "path": "website/docs/r/data_factory.html.markdown", + "slug": "data_factory", + "category": "resources", + "subcategory": "Data Factory" + }, + { + "id": "27680", + "title": "data_factory_dataset_mysql", + "path": "website/docs/r/data_factory_dataset_mysql.html.markdown", + "slug": "data_factory_dataset_mysql", + "category": "resources", + "subcategory": "Data Factory" + }, + { + "id": "27681", + "title": "data_factory_dataset_postgresql", + "path": "website/docs/r/data_factory_dataset_postgresql.html.markdown", + "slug": "data_factory_dataset_postgresql", + "category": "resources", + "subcategory": "Data Factory" + }, + { + "id": "27682", + "title": "data_factory_dataset_sql_server_table", + "path": "website/docs/r/data_factory_dataset_sql_server_table.html.markdown", + "slug": "data_factory_dataset_sql_server_table", + "category": "resources", + "subcategory": "Data Factory" + }, + { + "id": "27683", + "title": "data_factory_integration_runtime_managed", + "path": "website/docs/r/data_factory_integration_runtime_managed.html.markdown", + "slug": "data_factory_integration_runtime_managed", + "category": "resources", + "subcategory": "Data Factory" + }, + { + "id": "27684", + "title": "data_factory_linked_service_data_lake_storage_gen2", + "path": "website/docs/r/data_factory_linked_service_data_lake_storage_gen2.html.markdown", + "slug": "data_factory_linked_service_data_lake_storage_gen2", + "category": "resources", + "subcategory": "Data Factory" + }, + { + "id": "27685", + "title": "data_factory_linked_service_mysql", + "path": "website/docs/r/data_factory_linked_service_mysql.html.markdown", + "slug": "data_factory_linked_service_mysql", + "category": "resources", + "subcategory": "Data Factory" + }, + { + "id": "27686", + "title": "data_factory_linked_service_postgresql", + "path": "website/docs/r/data_factory_linked_service_postgresql.html.markdown", + "slug": "data_factory_linked_service_postgresql", + "category": "resources", + "subcategory": "Data Factory" + }, + { + "id": "27687", + "title": "data_factory_linked_service_sql_server", + "path": "website/docs/r/data_factory_linked_service_sql_server.html.markdown", + "slug": "data_factory_linked_service_sql_server", + "category": "resources", + "subcategory": "Data Factory" + }, + { + "id": "27688", + "title": "data_factory_pipeline", + "path": "website/docs/r/data_factory_pipeline.html.markdown", + "slug": "data_factory_pipeline", + "category": "resources", + "subcategory": "Data Factory" + }, + { + "id": "27689", + "title": "data_factory_trigger_schedule", + "path": "website/docs/r/data_factory_trigger_schedule.html.markdown", + "slug": "data_factory_trigger_schedule", + "category": "resources", + "subcategory": "Data Factory" + }, + { + "id": "27690", + "title": "data_lake_analytics_account", + "path": "website/docs/r/data_lake_analytics_account.html.markdown", + "slug": "data_lake_analytics_account", + "category": "resources", + "subcategory": "Data Lake" + }, + { + "id": "27691", + "title": "data_lake_analytics_firewall_rule", + "path": "website/docs/r/data_lake_analytics_firewall_rule.html.markdown", + "slug": "data_lake_analytics_firewall_rule", + "category": "resources", + "subcategory": "Data Lake" + }, + { + "id": "27692", + "title": "data_lake_store", + "path": "website/docs/r/data_lake_store.html.markdown", + "slug": "data_lake_store", + "category": "resources", + "subcategory": "Data Lake" + }, + { + "id": "27693", + "title": "data_lake_store_file", + "path": "website/docs/r/data_lake_store_file.html.markdown", + "slug": "data_lake_store_file", + "category": "resources", + "subcategory": "Data Lake" + }, + { + "id": "27694", + "title": "data_lake_store_firewall_rule", + "path": "website/docs/r/data_lake_store_firewall_rule.html.markdown", + "slug": "data_lake_store_firewall_rule", + "category": "resources", + "subcategory": "Data Lake" + }, + { + "id": "27695", + "title": "databricks_workspace", + "path": "website/docs/r/databricks_workspace.html.markdown", + "slug": "databricks_workspace", + "category": "resources", + "subcategory": "Databricks" + }, + { + "id": "27696", + "title": "ddos_protection_plan", + "path": "website/docs/r/ddos_protection_plan.html.markdown", + "slug": "ddos_protection_plan", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27697", + "title": "dev_test_lab", + "path": "website/docs/r/dev_test_lab.html.markdown", + "slug": "dev_test_lab", + "category": "resources", + "subcategory": "Dev Test" + }, + { + "id": "27698", + "title": "dev_test_linux_virtual_machine", + "path": "website/docs/r/dev_test_linux_virtual_machine.html.markdown", + "slug": "dev_test_linux_virtual_machine", + "category": "resources", + "subcategory": "Dev Test" + }, + { + "id": "27699", + "title": "dev_test_policy", + "path": "website/docs/r/dev_test_policy.html.markdown", + "slug": "dev_test_policy", + "category": "resources", + "subcategory": "Dev Test" + }, + { + "id": "27700", + "title": "dev_test_schedule", + "path": "website/docs/r/dev_test_schedule.html.markdown", + "slug": "dev_test_schedule", + "category": "resources", + "subcategory": "Dev Test" + }, + { + "id": "27701", + "title": "dev_test_virtual_network", + "path": "website/docs/r/dev_test_virtual_network.html.markdown", + "slug": "dev_test_virtual_network", + "category": "resources", + "subcategory": "Dev Test" + }, + { + "id": "27702", + "title": "dev_test_windows_virtual_machine", + "path": "website/docs/r/dev_test_windows_virtual_machine.html.markdown", + "slug": "dev_test_windows_virtual_machine", + "category": "resources", + "subcategory": "Dev Test" + }, + { + "id": "27703", + "title": "devspace_controller", + "path": "website/docs/r/devspace_controller.html.markdown", + "slug": "devspace_controller", + "category": "resources", + "subcategory": "DevSpace" + }, + { + "id": "27704", + "title": "dns_a_record", + "path": "website/docs/r/dns_a_record.html.markdown", + "slug": "dns_a_record", + "category": "resources", + "subcategory": "DNS" + }, + { + "id": "27705", + "title": "dns_aaaa_record", + "path": "website/docs/r/dns_aaaa_record.html.markdown", + "slug": "dns_aaaa_record", + "category": "resources", + "subcategory": "DNS" + }, + { + "id": "27706", + "title": "dns_caa_record", + "path": "website/docs/r/dns_caa_record.html.markdown", + "slug": "dns_caa_record", + "category": "resources", + "subcategory": "DNS" + }, + { + "id": "27707", + "title": "dns_cname_record", + "path": "website/docs/r/dns_cname_record.html.markdown", + "slug": "dns_cname_record", + "category": "resources", + "subcategory": "DNS" + }, + { + "id": "27708", + "title": "dns_mx_record", + "path": "website/docs/r/dns_mx_record.html.markdown", + "slug": "dns_mx_record", + "category": "resources", + "subcategory": "DNS" + }, + { + "id": "27709", + "title": "dns_ns_record", + "path": "website/docs/r/dns_ns_record.html.markdown", + "slug": "dns_ns_record", + "category": "resources", + "subcategory": "DNS" + }, + { + "id": "27710", + "title": "dns_ptr_record", + "path": "website/docs/r/dns_ptr_record.html.markdown", + "slug": "dns_ptr_record", + "category": "resources", + "subcategory": "DNS" + }, + { + "id": "27711", + "title": "dns_srv_record", + "path": "website/docs/r/dns_srv_record.html.markdown", + "slug": "dns_srv_record", + "category": "resources", + "subcategory": "DNS" + }, + { + "id": "27712", + "title": "dns_txt_record", + "path": "website/docs/r/dns_txt_record.html.markdown", + "slug": "dns_txt_record", + "category": "resources", + "subcategory": "DNS" + }, + { + "id": "27713", + "title": "dns_zone", + "path": "website/docs/r/dns_zone.html.markdown", + "slug": "dns_zone", + "category": "resources", + "subcategory": "DNS" + }, + { + "id": "27714", + "title": "eventgrid_domain", + "path": "website/docs/r/eventgrid_domain.html.markdown", + "slug": "eventgrid_domain", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27715", + "title": "eventgrid_event_subscription", + "path": "website/docs/r/eventgrid_event_subscription.html.markdown", + "slug": "eventgrid_event_subscription", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27716", + "title": "eventgrid_topic", + "path": "website/docs/r/eventgrid_topic.html.markdown", + "slug": "eventgrid_topic", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27717", + "title": "eventhub", + "path": "website/docs/r/eventhub.html.markdown", + "slug": "eventhub", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27718", + "title": "eventhub_authorization_rule", + "path": "website/docs/r/eventhub_authorization_rule.html.markdown", + "slug": "eventhub_authorization_rule", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27719", + "title": "eventhub_consumer_group", + "path": "website/docs/r/eventhub_consumer_group.html.markdown", + "slug": "eventhub_consumer_group", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27720", + "title": "eventhub_namespace", + "path": "website/docs/r/eventhub_namespace.html.markdown", + "slug": "eventhub_namespace", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27721", + "title": "eventhub_namespace_authorization_rule", + "path": "website/docs/r/eventhub_namespace_authorization_rule.html.markdown", + "slug": "eventhub_namespace_authorization_rule", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27722", + "title": "eventhub_namespace_disaster_recovery_config", + "path": "website/docs/r/eventhub_namespace_disaster_recovery_config.html.markdown", + "slug": "eventhub_namespace_disaster_recovery_config", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27723", + "title": "express_route_circuit", + "path": "website/docs/r/express_route_circuit.html.markdown", + "slug": "express_route_circuit", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27724", + "title": "express_route_circuit_authorization", + "path": "website/docs/r/express_route_circuit_authorization.html.markdown", + "slug": "express_route_circuit_authorization", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27725", + "title": "express_route_circuit_peering", + "path": "website/docs/r/express_route_circuit_peering.html.markdown", + "slug": "express_route_circuit_peering", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27726", + "title": "firewall", + "path": "website/docs/r/firewall.html.markdown", + "slug": "firewall", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27727", + "title": "firewall_application_rule_collection", + "path": "website/docs/r/firewall_application_rule_collection.html.markdown", + "slug": "firewall_application_rule_collection", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27728", + "title": "firewall_nat_rule_collection", + "path": "website/docs/r/firewall_nat_rule_collection.html.markdown", + "slug": "firewall_nat_rule_collection", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27729", + "title": "firewall_network_rule_collection", + "path": "website/docs/r/firewall_network_rule_collection.html.markdown", + "slug": "firewall_network_rule_collection", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27730", + "title": "front_door", + "path": "website/docs/r/front_door.html.markdown", + "slug": "front_door", + "category": "resources", + "subcategory": "Front Door" + }, + { + "id": "27731", + "title": "front_door_firewall_policy", + "path": "website/docs/r/front_door_firewall_policy.html.markdown", + "slug": "front_door_firewall_policy", + "category": "resources", + "subcategory": "Front Door" + }, + { + "id": "27732", + "title": "function_app", + "path": "website/docs/r/function_app.html.markdown", + "slug": "function_app", + "category": "resources", + "subcategory": "App Service (Web Apps)" + }, + { + "id": "27733", + "title": "hdinsight_hadoop_cluster", + "path": "website/docs/r/hdinsight_hadoop_cluster.html.markdown", + "slug": "hdinsight_hadoop_cluster", + "category": "resources", + "subcategory": "HDInsight" + }, + { + "id": "27734", + "title": "hdinsight_hbase_cluster", + "path": "website/docs/r/hdinsight_hbase_cluster.html.markdown", + "slug": "hdinsight_hbase_cluster", + "category": "resources", + "subcategory": "HDInsight" + }, + { + "id": "27735", + "title": "hdinsight_interactive_query_cluster", + "path": "website/docs/r/hdinsight_interactive_query_cluster.html.markdown", + "slug": "hdinsight_interactive_query_cluster", + "category": "resources", + "subcategory": "HDInsight" + }, + { + "id": "27736", + "title": "hdinsight_kafka_cluster", + "path": "website/docs/r/hdinsight_kafka_cluster.html.markdown", + "slug": "hdinsight_kafka_cluster", + "category": "resources", + "subcategory": "HDInsight" + }, + { + "id": "27737", + "title": "hdinsight_ml_services_cluster", + "path": "website/docs/r/hdinsight_ml_services_cluster.html.markdown", + "slug": "hdinsight_ml_services_cluster", + "category": "resources", + "subcategory": "HDInsight" + }, + { + "id": "27738", + "title": "hdinsight_rserver_cluster", + "path": "website/docs/r/hdinsight_rserver_cluster.html.markdown", + "slug": "hdinsight_rserver_cluster", + "category": "resources", + "subcategory": "HDInsight" + }, + { + "id": "27739", + "title": "hdinsight_spark_cluster", + "path": "website/docs/r/hdinsight_spark_cluster.html.markdown", + "slug": "hdinsight_spark_cluster", + "category": "resources", + "subcategory": "HDInsight" + }, + { + "id": "27740", + "title": "hdinsight_storm_cluster", + "path": "website/docs/r/hdinsight_storm_cluster.html.markdown", + "slug": "hdinsight_storm_cluster", + "category": "resources", + "subcategory": "HDInsight" + }, + { + "id": "27741", + "title": "healthcare_service", + "path": "website/docs/r/healthcare_service.html.markdown", + "slug": "healthcare_service", + "category": "resources", + "subcategory": "Healthcare API" + }, + { + "id": "27742", + "title": "image", + "path": "website/docs/r/image.html.markdown", + "slug": "image", + "category": "resources", + "subcategory": "Compute" + }, + { + "id": "27743", + "title": "iothub", + "path": "website/docs/r/iothub.html.markdown", + "slug": "iothub", + "category": "resources", + "subcategory": "IoT Hub" + }, + { + "id": "27744", + "title": "iothub_consumer_group", + "path": "website/docs/r/iothub_consumer_group.html.markdown", + "slug": "iothub_consumer_group", + "category": "resources", + "subcategory": "IoT Hub" + }, + { + "id": "27745", + "title": "iothub_dps", + "path": "website/docs/r/iothub_dps.html.markdown", + "slug": "iothub_dps", + "category": "resources", + "subcategory": "IoT Hub" + }, + { + "id": "27746", + "title": "iothub_dps_certificate", + "path": "website/docs/r/iothub_dps_certificate.html.markdown", + "slug": "iothub_dps_certificate", + "category": "resources", + "subcategory": "IoT Hub" + }, + { + "id": "27747", + "title": "iothub_endpoint_eventhub", + "path": "website/docs/r/iothub_endpoint_eventhub.html.markdown", + "slug": "iothub_endpoint_eventhub", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27748", + "title": "iothub_endpoint_servicebus_queue", + "path": "website/docs/r/iothub_endpoint_servicebus_queue.html.markdown", + "slug": "iothub_endpoint_servicebus_queue", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27749", + "title": "iothub_endpoint_servicebus_topic", + "path": "website/docs/r/iothub_endpoint_servicebus_topic.html.markdown", + "slug": "iothub_endpoint_servicebus_topic", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27750", + "title": "iothub_endpoint_storage_container", + "path": "website/docs/r/iothub_endpoint_storage_container.html.markdown", + "slug": "iothub_endpoint_storage_container", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27751", + "title": "iothub_route", + "path": "website/docs/r/iothub_route.html.markdown", + "slug": "iothub_route", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27752", + "title": "iothub_shared_access_policy", + "path": "website/docs/r/iothub_shared_access_policy.html.markdown", + "slug": "iothub_shared_access_policy", + "category": "resources", + "subcategory": "IoT Hub" + }, + { + "id": "27753", + "title": "key_vault", + "path": "website/docs/r/key_vault.html.markdown", + "slug": "key_vault", + "category": "resources", + "subcategory": "Key Vault" + }, + { + "id": "27754", + "title": "key_vault_access_policy", + "path": "website/docs/r/key_vault_access_policy.html.markdown", + "slug": "key_vault_access_policy", + "category": "resources", + "subcategory": "Key Vault" + }, + { + "id": "27755", + "title": "key_vault_certificate", + "path": "website/docs/r/key_vault_certificate.html.markdown", + "slug": "key_vault_certificate", + "category": "resources", + "subcategory": "Key Vault" + }, + { + "id": "27756", + "title": "key_vault_key", + "path": "website/docs/r/key_vault_key.html.markdown", + "slug": "key_vault_key", + "category": "resources", + "subcategory": "Key Vault" + }, + { + "id": "27757", + "title": "key_vault_secret", + "path": "website/docs/r/key_vault_secret.html.markdown", + "slug": "key_vault_secret", + "category": "resources", + "subcategory": "Key Vault" + }, + { + "id": "27758", + "title": "kubernetes_cluster", + "path": "website/docs/r/kubernetes_cluster.html.markdown", + "slug": "kubernetes_cluster", + "category": "resources", + "subcategory": "Container" + }, + { + "id": "27759", + "title": "kubernetes_cluster_node_pool", + "path": "website/docs/r/kubernetes_cluster_node_pool.html.markdown", + "slug": "kubernetes_cluster_node_pool", + "category": "resources", + "subcategory": "Container" + }, + { + "id": "27760", + "title": "kusto_cluster", + "path": "website/docs/r/kusto_cluster.html.markdown", + "slug": "kusto_cluster", + "category": "resources", + "subcategory": "Data Explorer" + }, + { + "id": "27761", + "title": "kusto_database", + "path": "website/docs/r/kusto_database.html.markdown", + "slug": "kusto_database", + "category": "resources", + "subcategory": "Data Explorer" + }, + { + "id": "27762", + "title": "kusto_eventhub_data_connection", + "path": "website/docs/r/kusto_eventhub_data_connection.html.markdown", + "slug": "kusto_eventhub_data_connection", + "category": "resources", + "subcategory": "Data Explorer" + }, + { + "id": "27763", + "title": "linux_virtual_machine_scale_set", + "path": "website/docs/r/linux_virtual_machine_scale_set.html.markdown", + "slug": "linux_virtual_machine_scale_set", + "category": "resources", + "subcategory": "Beta" + }, + { + "id": "27764", + "title": "loadbalancer", + "path": "website/docs/r/loadbalancer.html.markdown", + "slug": "loadbalancer", + "category": "resources", + "subcategory": "Load Balancer" + }, + { + "id": "27765", + "title": "loadbalancer_backend_address_pool", + "path": "website/docs/r/loadbalancer_backend_address_pool.html.markdown", + "slug": "loadbalancer_backend_address_pool", + "category": "resources", + "subcategory": "Load Balancer" + }, + { + "id": "27766", + "title": "loadbalancer_nat_pool", + "path": "website/docs/r/loadbalancer_nat_pool.html.markdown", + "slug": "loadbalancer_nat_pool", + "category": "resources", + "subcategory": "Load Balancer" + }, + { + "id": "27767", + "title": "loadbalancer_nat_rule", + "path": "website/docs/r/loadbalancer_nat_rule.html.markdown", + "slug": "loadbalancer_nat_rule", + "category": "resources", + "subcategory": "Load Balancer" + }, + { + "id": "27768", + "title": "loadbalancer_outbound_rule", + "path": "website/docs/r/loadbalancer_outbound_rule.html.markdown", + "slug": "loadbalancer_outbound_rule", + "category": "resources", + "subcategory": "Load Balancer" + }, + { + "id": "27769", + "title": "loadbalancer_probe", + "path": "website/docs/r/loadbalancer_probe.html.markdown", + "slug": "loadbalancer_probe", + "category": "resources", + "subcategory": "Load Balancer" + }, + { + "id": "27770", + "title": "loadbalancer_rule", + "path": "website/docs/r/loadbalancer_rule.html.markdown", + "slug": "loadbalancer_rule", + "category": "resources", + "subcategory": "Load Balancer" + }, + { + "id": "27771", + "title": "local_network_gateway", + "path": "website/docs/r/local_network_gateway.html.markdown", + "slug": "local_network_gateway", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27772", + "title": "log_analytics_linked_service", + "path": "website/docs/r/log_analytics_linked_service.html.markdown", + "slug": "log_analytics_linked_service", + "category": "resources", + "subcategory": "Log Analytics" + }, + { + "id": "27773", + "title": "log_analytics_solution", + "path": "website/docs/r/log_analytics_solution.html.markdown", + "slug": "log_analytics_solution", + "category": "resources", + "subcategory": "Log Analytics" + }, + { + "id": "27774", + "title": "log_analytics_workspace", + "path": "website/docs/r/log_analytics_workspace.html.markdown", + "slug": "log_analytics_workspace", + "category": "resources", + "subcategory": "Log Analytics" + }, + { + "id": "27775", + "title": "log_analytics_workspace_linked_service", + "path": "website/docs/r/log_analytics_workspace_linked_service.html.markdown", + "slug": "log_analytics_workspace_linked_service", + "category": "resources", + "subcategory": "Log Analytics" + }, + { + "id": "27776", + "title": "logic_app_action_custom", + "path": "website/docs/r/logic_app_action_custom.html.markdown", + "slug": "logic_app_action_custom", + "category": "resources", + "subcategory": "Logic App" + }, + { + "id": "27777", + "title": "logic_app_action_http", + "path": "website/docs/r/logic_app_action_http.html.markdown", + "slug": "logic_app_action_http", + "category": "resources", + "subcategory": "Logic App" + }, + { + "id": "27778", + "title": "logic_app_trigger_custom", + "path": "website/docs/r/logic_app_trigger_custom.html.markdown", + "slug": "logic_app_trigger_custom", + "category": "resources", + "subcategory": "Logic App" + }, + { + "id": "27779", + "title": "logic_app_trigger_http_request", + "path": "website/docs/r/logic_app_trigger_http_request.html.markdown", + "slug": "logic_app_trigger_http_request", + "category": "resources", + "subcategory": "Logic App" + }, + { + "id": "27780", + "title": "logic_app_trigger_recurrence", + "path": "website/docs/r/logic_app_trigger_recurrence.html.markdown", + "slug": "logic_app_trigger_recurrence", + "category": "resources", + "subcategory": "Logic App" + }, + { + "id": "27781", + "title": "logic_app_workflow", + "path": "website/docs/r/logic_app_workflow.html.markdown", + "slug": "logic_app_workflow", + "category": "resources", + "subcategory": "Logic App" + }, + { + "id": "27782", + "title": "managed_disk", + "path": "website/docs/r/managed_disk.html.markdown", + "slug": "managed_disk", + "category": "resources", + "subcategory": "Compute" + }, + { + "id": "27783", + "title": "management_group", + "path": "website/docs/r/management_group.html.markdown", + "slug": "management_group", + "category": "resources", + "subcategory": "Management" + }, + { + "id": "27784", + "title": "management_lock", + "path": "website/docs/r/management_lock.html.markdown", + "slug": "management_lock", + "category": "resources", + "subcategory": "Management" + }, + { + "id": "27785", + "title": "maps_account", + "path": "website/docs/r/maps_account.html.markdown", + "slug": "maps_account", + "category": "resources", + "subcategory": "Maps" + }, + { + "id": "27786", + "title": "mariadb_configuration", + "path": "website/docs/r/mariadb_configuration.html.markdown", + "slug": "mariadb_configuration", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27787", + "title": "mariadb_database", + "path": "website/docs/r/mariadb_database.html.markdown", + "slug": "mariadb_database", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27788", + "title": "mariadb_firewall_rule", + "path": "website/docs/r/mariadb_firewall_rule.html.markdown", + "slug": "mariadb_firewall_rule", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27789", + "title": "mariadb_server", + "path": "website/docs/r/mariadb_server.html.markdown", + "slug": "mariadb_server", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27790", + "title": "mariadb_virtual_network_rule", + "path": "website/docs/r/mariadb_virtual_network_rule.html.markdown", + "slug": "mariadb_virtual_network_rule", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27791", + "title": "marketplace_agreement", + "path": "website/docs/r/marketplace_agreement.html.markdown", + "slug": "marketplace_agreement", + "category": "resources", + "subcategory": "Compute" + }, + { + "id": "27792", + "title": "media_services_account", + "path": "website/docs/r/media_services_account.html.markdown", + "slug": "media_services_account", + "category": "resources", + "subcategory": "Media" + }, + { + "id": "27793", + "title": "metric_alertrule", + "path": "website/docs/r/metric_alertrule.html.markdown", + "slug": "metric_alertrule", + "category": "resources", + "subcategory": "Monitor" + }, + { + "id": "27794", + "title": "monitor_action_group", + "path": "website/docs/r/monitor_action_group.html.markdown", + "slug": "monitor_action_group", + "category": "resources", + "subcategory": "Monitor" + }, + { + "id": "27795", + "title": "monitor_activity_log_alert", + "path": "website/docs/r/monitor_activity_log_alert.html.markdown", + "slug": "monitor_activity_log_alert", + "category": "resources", + "subcategory": "Monitor" + }, + { + "id": "27796", + "title": "monitor_autoscale_setting", + "path": "website/docs/r/monitor_autoscale_setting.html.markdown", + "slug": "monitor_autoscale_setting", + "category": "resources", + "subcategory": "Monitor" + }, + { + "id": "27797", + "title": "monitor_diagnostic_setting", + "path": "website/docs/r/monitor_diagnostic_setting.html.markdown", + "slug": "monitor_diagnostic_setting", + "category": "resources", + "subcategory": "Monitor" + }, + { + "id": "27798", + "title": "monitor_log_profile", + "path": "website/docs/r/monitor_log_profile.html.markdown", + "slug": "monitor_log_profile", + "category": "resources", + "subcategory": "Monitor" + }, + { + "id": "27799", + "title": "monitor_metric_alert", + "path": "website/docs/r/monitor_metric_alert.html.markdown", + "slug": "monitor_metric_alert", + "category": "resources", + "subcategory": "Monitor" + }, + { + "id": "27800", + "title": "monitor_metric_alertrule", + "path": "website/docs/r/monitor_metric_alertrule.html.markdown", + "slug": "monitor_metric_alertrule", + "category": "resources", + "subcategory": "Monitor" + }, + { + "id": "27801", + "title": "mssql_elasticpool", + "path": "website/docs/r/mssql_elasticpool.html.markdown", + "slug": "mssql_elasticpool", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27802", + "title": "mysql_configuration", + "path": "website/docs/r/mysql_configuration.html.markdown", + "slug": "mysql_configuration", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27803", + "title": "mysql_database", + "path": "website/docs/r/mysql_database.html.markdown", + "slug": "mysql_database", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27804", + "title": "mysql_firewall_rule", + "path": "website/docs/r/mysql_firewall_rule.html.markdown", + "slug": "mysql_firewall_rule", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27805", + "title": "mysql_server", + "path": "website/docs/r/mysql_server.html.markdown", + "slug": "mysql_server", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27806", + "title": "mysql_virtual_network_rule", + "path": "website/docs/r/mysql_virtual_network_rule.html.markdown", + "slug": "mysql_virtual_network_rule", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27807", + "title": "netapp_account", + "path": "website/docs/r/netapp_account.html.markdown", + "slug": "netapp_account", + "category": "resources", + "subcategory": "NetApp" + }, + { + "id": "27808", + "title": "netapp_pool", + "path": "website/docs/r/netapp_pool.html.markdown", + "slug": "netapp_pool", + "category": "resources", + "subcategory": "NetApp" + }, + { + "id": "27809", + "title": "network_connection_monitor", + "path": "website/docs/r/network_connection_monitor.html.markdown", + "slug": "network_connection_monitor", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27810", + "title": "network_ddos_protection_plan", + "path": "website/docs/r/network_ddos_protection_plan.html.markdown", + "slug": "network_ddos_protection_plan", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27811", + "title": "network_interface", + "path": "website/docs/r/network_interface.html.markdown", + "slug": "network_interface", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27812", + "title": "network_interface_application_gateway_backend_address_pool_association", + "path": "website/docs/r/network_interface_application_gateway_backend_address_pool_association.html.markdown", + "slug": "network_interface_application_gateway_backend_address_pool_association", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27813", + "title": "network_interface_application_security_group_association", + "path": "website/docs/r/network_interface_application_security_group_association.html.markdown", + "slug": "network_interface_application_security_group_association", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27814", + "title": "network_interface_backend_address_pool_association", + "path": "website/docs/r/network_interface_backend_address_pool_association.html.markdown", + "slug": "network_interface_backend_address_pool_association", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27815", + "title": "network_interface_nat_rule_association", + "path": "website/docs/r/network_interface_nat_rule_association.html.markdown", + "slug": "network_interface_nat_rule_association", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27816", + "title": "network_packet_capture", + "path": "website/docs/r/network_packet_capture.html.markdown", + "slug": "network_packet_capture", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27817", + "title": "network_profile", + "path": "website/docs/r/network_profile.html.markdown", + "slug": "network_profile", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27818", + "title": "network_security_group", + "path": "website/docs/r/network_security_group.html.markdown", + "slug": "network_security_group", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27819", + "title": "network_security_rule", + "path": "website/docs/r/network_security_rule.html.markdown", + "slug": "network_security_rule", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27820", + "title": "network_watcher", + "path": "website/docs/r/network_watcher.html.markdown", + "slug": "network_watcher", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27821", + "title": "notification_hub", + "path": "website/docs/r/notification_hub.html.markdown", + "slug": "notification_hub", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27822", + "title": "notification_hub_authorization_rule", + "path": "website/docs/r/notification_hub_authorization_rule.html.markdown", + "slug": "notification_hub_authorization_rule", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27823", + "title": "notification_hub_namespace", + "path": "website/docs/r/notification_hub_namespace.html.markdown", + "slug": "notification_hub_namespace", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27824", + "title": "packet_capture", + "path": "website/docs/r/packet_capture.html.markdown", + "slug": "packet_capture", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27825", + "title": "policy_assignment", + "path": "website/docs/r/policy_assignment.html.markdown", + "slug": "policy_assignment", + "category": "resources", + "subcategory": "Policy" + }, + { + "id": "27826", + "title": "policy_definition", + "path": "website/docs/r/policy_definition.html.markdown", + "slug": "policy_definition", + "category": "resources", + "subcategory": "Policy" + }, + { + "id": "27827", + "title": "policy_set_definition", + "path": "website/docs/r/policy_set_definition.html.markdown", + "slug": "policy_set_definition", + "category": "resources", + "subcategory": "Policy" + }, + { + "id": "27828", + "title": "postgresql_configuration", + "path": "website/docs/r/postgresql_configuration.html.markdown", + "slug": "postgresql_configuration", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27829", + "title": "postgresql_database", + "path": "website/docs/r/postgresql_database.html.markdown", + "slug": "postgresql_database", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27830", + "title": "postgresql_firewall_rule", + "path": "website/docs/r/postgresql_firewall_rule.html.markdown", + "slug": "postgresql_firewall_rule", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27831", + "title": "postgresql_server", + "path": "website/docs/r/postgresql_server.html.markdown", + "slug": "postgresql_server", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27832", + "title": "postgresql_virtual_network_rule", + "path": "website/docs/r/postgresql_virtual_network_rule.html.markdown", + "slug": "postgresql_virtual_network_rule", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27833", + "title": "private_dns_a_record", + "path": "website/docs/r/private_dns_a_record.html.markdown", + "slug": "private_dns_a_record", + "category": "resources", + "subcategory": "Private DNS" + }, + { + "id": "27834", + "title": "private_dns_aaaa_record", + "path": "website/docs/r/private_dns_aaaa_record.html.markdown", + "slug": "private_dns_aaaa_record", + "category": "resources", + "subcategory": "Private DNS" + }, + { + "id": "27835", + "title": "private_dns_cname_record", + "path": "website/docs/r/private_dns_cname_record.html.markdown", + "slug": "private_dns_cname_record", + "category": "resources", + "subcategory": "Private DNS" + }, + { + "id": "27836", + "title": "private_dns_ptr_record", + "path": "website/docs/r/private_dns_ptr_record.html.markdown", + "slug": "private_dns_ptr_record", + "category": "resources", + "subcategory": "Private DNS" + }, + { + "id": "27837", + "title": "private_dns_srv_record", + "path": "website/docs/r/private_dns_srv_record.html.markdown", + "slug": "private_dns_srv_record", + "category": "resources", + "subcategory": "Private DNS" + }, + { + "id": "27838", + "title": "private_dns_zone", + "path": "website/docs/r/private_dns_zone.html.markdown", + "slug": "private_dns_zone", + "category": "resources", + "subcategory": "Private DNS" + }, + { + "id": "27839", + "title": "private_dns_zone_virtual_network_link", + "path": "website/docs/r/private_dns_zone_virtual_network_link.html.markdown", + "slug": "private_dns_zone_virtual_network_link", + "category": "resources", + "subcategory": "Private DNS" + }, + { + "id": "27840", + "title": "private_link_service", + "path": "website/docs/r/private_link_service.html.markdown", + "slug": "private_link_service", + "category": "resources", + "subcategory": "" + }, + { + "id": "27841", + "title": "proximity_placement_group", + "path": "website/docs/r/proximity_placement_group.html.markdown", + "slug": "proximity_placement_group", + "category": "resources", + "subcategory": "Compute" + }, + { + "id": "27842", + "title": "public_ip", + "path": "website/docs/r/public_ip.html.markdown", + "slug": "public_ip", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27843", + "title": "public_ip_prefix", + "path": "website/docs/r/public_ip_prefix.html.markdown", + "slug": "public_ip_prefix", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27844", + "title": "recovery_network_mapping", + "path": "website/docs/r/recovery_network_mapping.html.markdown", + "slug": "recovery_network_mapping", + "category": "resources", + "subcategory": "Recovery Services" + }, + { + "id": "27845", + "title": "recovery_services_fabric", + "path": "website/docs/r/recovery_services_fabric.html.markdown", + "slug": "recovery_services_fabric", + "category": "resources", + "subcategory": "Recovery Services" + }, + { + "id": "27846", + "title": "recovery_services_protected_vm", + "path": "website/docs/r/recovery_services_protected_vm.markdown", + "slug": "recovery_services_protected_vm", + "category": "resources", + "subcategory": "Recovery Services" + }, + { + "id": "27847", + "title": "recovery_services_protection_container", + "path": "website/docs/r/recovery_services_protection_container.html.markdown", + "slug": "recovery_services_protection_container", + "category": "resources", + "subcategory": "Recovery Services" + }, + { + "id": "27848", + "title": "recovery_services_protection_container_mapping", + "path": "website/docs/r/recovery_services_protection_container_mapping.html.markdown", + "slug": "recovery_services_protection_container_mapping", + "category": "resources", + "subcategory": "Recovery Services" + }, + { + "id": "27849", + "title": "recovery_services_protection_policy_vm", + "path": "website/docs/r/recovery_services_protection_policy_vm.markdown", + "slug": "recovery_services_protection_policy_vm", + "category": "resources", + "subcategory": "Recovery Services" + }, + { + "id": "27850", + "title": "recovery_services_replicated_vm", + "path": "website/docs/r/recovery_services_replicated_vm.html.markdown", + "slug": "recovery_services_replicated_vm", + "category": "resources", + "subcategory": "Recovery Services" + }, + { + "id": "27851", + "title": "recovery_services_replication_policy", + "path": "website/docs/r/recovery_services_replication_policy.html.markdown", + "slug": "recovery_services_replication_policy", + "category": "resources", + "subcategory": "Recovery Services" + }, + { + "id": "27852", + "title": "recovery_services_vault", + "path": "website/docs/r/recovery_services_vault.markdown", + "slug": "recovery_services_vault", + "category": "resources", + "subcategory": "Recovery Services" + }, + { + "id": "27853", + "title": "redis_cache", + "path": "website/docs/r/redis_cache.html.markdown", + "slug": "redis_cache", + "category": "resources", + "subcategory": "Redis" + }, + { + "id": "27854", + "title": "redis_firewall_rule", + "path": "website/docs/r/redis_firewall_rule.html.markdown", + "slug": "redis_firewall_rule", + "category": "resources", + "subcategory": "Redis" + }, + { + "id": "27855", + "title": "relay_hybrid_connection", + "path": "website/docs/r/relay_hybrid_connection.html.markdown", + "slug": "relay_hybrid_connection", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27856", + "title": "relay_namespace", + "path": "website/docs/r/relay_namespace.html.markdown", + "slug": "relay_namespace", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27857", + "title": "resource_group", + "path": "website/docs/r/resource_group.html.markdown", + "slug": "resource_group", + "category": "resources", + "subcategory": "Base" + }, + { + "id": "27858", + "title": "role_assignment", + "path": "website/docs/r/role_assignment.html.markdown", + "slug": "role_assignment", + "category": "resources", + "subcategory": "Authorization" + }, + { + "id": "27859", + "title": "role_definition", + "path": "website/docs/r/role_definition.html.markdown", + "slug": "role_definition", + "category": "resources", + "subcategory": "Authorization" + }, + { + "id": "27860", + "title": "route", + "path": "website/docs/r/route.html.markdown", + "slug": "route", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27861", + "title": "route_table", + "path": "website/docs/r/route_table.html.markdown", + "slug": "route_table", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27862", + "title": "scheduler_job", + "path": "website/docs/r/scheduler_job.html.markdown", + "slug": "scheduler_job", + "category": "resources", + "subcategory": "Scheduler" + }, + { + "id": "27863", + "title": "scheduler_job_collection", + "path": "website/docs/r/scheduler_job_collection.html.markdown", + "slug": "scheduler_job_collection", + "category": "resources", + "subcategory": "Scheduler" + }, + { + "id": "27864", + "title": "search_service", + "path": "website/docs/r/search_service.html.markdown", + "slug": "search_service", + "category": "resources", + "subcategory": "Search" + }, + { + "id": "27865", + "title": "security_center_contact", + "path": "website/docs/r/security_center_contact.markdown", + "slug": "security_center_contact", + "category": "resources", + "subcategory": "Security Center" + }, + { + "id": "27866", + "title": "security_center_subscription_pricing", + "path": "website/docs/r/security_center_subscription_pricing.markdown", + "slug": "security_center_subscription_pricing", + "category": "resources", + "subcategory": "Security Center" + }, + { + "id": "27867", + "title": "security_center_workspace", + "path": "website/docs/r/security_center_workspace.markdown", + "slug": "security_center_workspace", + "category": "resources", + "subcategory": "Security Center" + }, + { + "id": "27868", + "title": "service_fabric_cluster", + "path": "website/docs/r/service_fabric_cluster.html.markdown", + "slug": "service_fabric_cluster", + "category": "resources", + "subcategory": "Service Fabric" + }, + { + "id": "27869", + "title": "servicebus_namespace", + "path": "website/docs/r/servicebus_namespace.html.markdown", + "slug": "servicebus_namespace", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27870", + "title": "servicebus_namespace_authorization_rule", + "path": "website/docs/r/servicebus_namespace_authorization_rule.html.markdown", + "slug": "servicebus_namespace_authorization_rule", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27871", + "title": "servicebus_queue", + "path": "website/docs/r/servicebus_queue.html.markdown", + "slug": "servicebus_queue", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27872", + "title": "servicebus_queue_authorization_rule", + "path": "website/docs/r/servicebus_queue_authorization_rule.html.markdown", + "slug": "servicebus_queue_authorization_rule", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27873", + "title": "servicebus_subscription", + "path": "website/docs/r/servicebus_subscription.html.markdown", + "slug": "servicebus_subscription", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27874", + "title": "servicebus_subscription_rule", + "path": "website/docs/r/servicebus_subscription_rule.html.markdown", + "slug": "servicebus_subscription_rule", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27875", + "title": "servicebus_topic", + "path": "website/docs/r/servicebus_topic.html.markdown", + "slug": "servicebus_topic", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27876", + "title": "servicebus_topic_authorization_rule", + "path": "website/docs/r/servicebus_topic_authorization_rule.html.markdown", + "slug": "servicebus_topic_authorization_rule", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27877", + "title": "shared_image", + "path": "website/docs/r/shared_image.html.markdown", + "slug": "shared_image", + "category": "resources", + "subcategory": "Compute" + }, + { + "id": "27878", + "title": "shared_image_gallery", + "path": "website/docs/r/shared_image_gallery.html.markdown", + "slug": "shared_image_gallery", + "category": "resources", + "subcategory": "Compute" + }, + { + "id": "27879", + "title": "shared_image_version", + "path": "website/docs/r/shared_image_version.html.markdown", + "slug": "shared_image_version", + "category": "resources", + "subcategory": "Compute" + }, + { + "id": "27880", + "title": "signalr_service", + "path": "website/docs/r/signalr_service.html.markdown", + "slug": "signalr_service", + "category": "resources", + "subcategory": "Messaging" + }, + { + "id": "27881", + "title": "snapshot", + "path": "website/docs/r/snapshot.html.markdown", + "slug": "snapshot", + "category": "resources", + "subcategory": "Compute" + }, + { + "id": "27882", + "title": "sql_active_directory_administrator", + "path": "website/docs/r/sql_active_directory_administrator.markdown", + "slug": "sql_active_directory_administrator", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27883", + "title": "sql_database", + "path": "website/docs/r/sql_database.html.markdown", + "slug": "sql_database", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27884", + "title": "sql_elasticpool", + "path": "website/docs/r/sql_elasticpool.html.markdown", + "slug": "sql_elasticpool", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27885", + "title": "sql_failover_group", + "path": "website/docs/r/sql_failover_group.html.markdown", + "slug": "sql_failover_group", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27886", + "title": "sql_firewall_rule", + "path": "website/docs/r/sql_firewall_rule.html.markdown", + "slug": "sql_firewall_rule", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27887", + "title": "sql_server", + "path": "website/docs/r/sql_server.html.markdown", + "slug": "sql_server", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27888", + "title": "sql_virtual_network_rule", + "path": "website/docs/r/sql_virtual_network_rule.html.markdown", + "slug": "sql_virtual_network_rule", + "category": "resources", + "subcategory": "Database" + }, + { + "id": "27889", + "title": "storage_account", + "path": "website/docs/r/storage_account.html.markdown", + "slug": "storage_account", + "category": "resources", + "subcategory": "Storage" + }, + { + "id": "27890", + "title": "storage_blob", + "path": "website/docs/r/storage_blob.html.markdown", + "slug": "storage_blob", + "category": "resources", + "subcategory": "Storage" + }, + { + "id": "27891", + "title": "storage_container", + "path": "website/docs/r/storage_container.html.markdown", + "slug": "storage_container", + "category": "resources", + "subcategory": "Storage" + }, + { + "id": "27892", + "title": "storage_data_lake_gen2_filesystem", + "path": "website/docs/r/storage_data_lake_gen2_filesystem.html.markdown", + "slug": "storage_data_lake_gen2_filesystem", + "category": "resources", + "subcategory": "Storage" + }, + { + "id": "27893", + "title": "storage_management_policy", + "path": "website/docs/r/storage_management_policy.html.markdown", + "slug": "storage_management_policy", + "category": "resources", + "subcategory": "Storage" + }, + { + "id": "27894", + "title": "storage_queue", + "path": "website/docs/r/storage_queue.html.markdown", + "slug": "storage_queue", + "category": "resources", + "subcategory": "Storage" + }, + { + "id": "27895", + "title": "storage_share", + "path": "website/docs/r/storage_share.html.markdown", + "slug": "storage_share", + "category": "resources", + "subcategory": "Storage" + }, + { + "id": "27896", + "title": "storage_share_directory", + "path": "website/docs/r/storage_share_directory.html.markdown", + "slug": "storage_share_directory", + "category": "resources", + "subcategory": "Storage" + }, + { + "id": "27897", + "title": "storage_table", + "path": "website/docs/r/storage_table.html.markdown", + "slug": "storage_table", + "category": "resources", + "subcategory": "Storage" + }, + { + "id": "27898", + "title": "storage_table_entity", + "path": "website/docs/r/storage_table_entity.html.markdown", + "slug": "storage_table_entity", + "category": "resources", + "subcategory": "Storage" + }, + { + "id": "27899", + "title": "stream_analytics_function_javascript_udf", + "path": "website/docs/r/stream_analytics_function_javascript_udf.html.markdown", + "slug": "stream_analytics_function_javascript_udf", + "category": "resources", + "subcategory": "Stream Analytics" + }, + { + "id": "27900", + "title": "stream_analytics_job", + "path": "website/docs/r/stream_analytics_job.html.markdown", + "slug": "stream_analytics_job", + "category": "resources", + "subcategory": "Stream Analytics" + }, + { + "id": "27901", + "title": "stream_analytics_output_blob", + "path": "website/docs/r/stream_analytics_output_blob.html.markdown", + "slug": "stream_analytics_output_blob", + "category": "resources", + "subcategory": "Stream Analytics" + }, + { + "id": "27902", + "title": "stream_analytics_output_eventhub", + "path": "website/docs/r/stream_analytics_output_eventhub.html.markdown", + "slug": "stream_analytics_output_eventhub", + "category": "resources", + "subcategory": "Stream Analytics" + }, + { + "id": "27903", + "title": "stream_analytics_output_mssql", + "path": "website/docs/r/stream_analytics_output_mssql.html.markdown", + "slug": "stream_analytics_output_mssql", + "category": "resources", + "subcategory": "Stream Analytics" + }, + { + "id": "27904", + "title": "stream_analytics_output_servicebus_queue", + "path": "website/docs/r/stream_analytics_output_servicebus_queue.html.markdown", + "slug": "stream_analytics_output_servicebus_queue", + "category": "resources", + "subcategory": "Stream Analytics" + }, + { + "id": "27905", + "title": "stream_analytics_output_servicebus_topic", + "path": "website/docs/r/stream_analytics_output_servicebus_topic.html.markdown", + "slug": "stream_analytics_output_servicebus_topic", + "category": "resources", + "subcategory": "Stream Analytics" + }, + { + "id": "27906", + "title": "stream_analytics_stream_input_blob", + "path": "website/docs/r/stream_analytics_stream_input_blob.html.markdown", + "slug": "stream_analytics_stream_input_blob", + "category": "resources", + "subcategory": "Stream Analytics" + }, + { + "id": "27907", + "title": "stream_analytics_stream_input_eventhub", + "path": "website/docs/r/stream_analytics_stream_input_eventhub.html.markdown", + "slug": "stream_analytics_stream_input_eventhub", + "category": "resources", + "subcategory": "Stream Analytics" + }, + { + "id": "27908", + "title": "stream_analytics_stream_input_iothub", + "path": "website/docs/r/stream_analytics_stream_input_iothub.html.markdown", + "slug": "stream_analytics_stream_input_iothub", + "category": "resources", + "subcategory": "Stream Analytics" + }, + { + "id": "27909", + "title": "subnet", + "path": "website/docs/r/subnet.html.markdown", + "slug": "subnet", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27910", + "title": "subnet_network_security_group_association", + "path": "website/docs/r/subnet_network_security_group_association.html.markdown", + "slug": "subnet_network_security_group_association", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27911", + "title": "subnet_route_table_association", + "path": "website/docs/r/subnet_route_table_association.html.markdown", + "slug": "subnet_route_table_association", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27912", + "title": "template_deployment", + "path": "website/docs/r/template_deployment.html.markdown", + "slug": "template_deployment", + "category": "resources", + "subcategory": "Template" + }, + { + "id": "27913", + "title": "traffic_manager_endpoint", + "path": "website/docs/r/traffic_manager_endpoint.html.markdown", + "slug": "traffic_manager_endpoint", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27914", + "title": "traffic_manager_profile", + "path": "website/docs/r/traffic_manager_profile.html.markdown", + "slug": "traffic_manager_profile", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27915", + "title": "user_assigned_identity", + "path": "website/docs/r/user_assigned_identity.markdown", + "slug": "user_assigned_identity", + "category": "resources", + "subcategory": "Authorization" + }, + { + "id": "27916", + "title": "virtual_machine", + "path": "website/docs/r/virtual_machine.html.markdown", + "slug": "virtual_machine", + "category": "resources", + "subcategory": "Compute" + }, + { + "id": "27917", + "title": "virtual_machine_data_disk_attachment", + "path": "website/docs/r/virtual_machine_data_disk_attachment.html.markdown", + "slug": "virtual_machine_data_disk_attachment", + "category": "resources", + "subcategory": "Compute" + }, + { + "id": "27918", + "title": "virtual_machine_extension", + "path": "website/docs/r/virtual_machine_extension.html.markdown", + "slug": "virtual_machine_extension", + "category": "resources", + "subcategory": "Compute" + }, + { + "id": "27919", + "title": "virtual_machine_scale_set", + "path": "website/docs/r/virtual_machine_scale_set.html.markdown", + "slug": "virtual_machine_scale_set", + "category": "resources", + "subcategory": "Compute" + }, + { + "id": "27920", + "title": "virtual_network", + "path": "website/docs/r/virtual_network.html.markdown", + "slug": "virtual_network", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27921", + "title": "virtual_network_gateway", + "path": "website/docs/r/virtual_network_gateway.html.markdown", + "slug": "virtual_network_gateway", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27922", + "title": "virtual_network_gateway_connection", + "path": "website/docs/r/virtual_network_gateway_connection.html.markdown", + "slug": "virtual_network_gateway_connection", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27923", + "title": "virtual_network_peering", + "path": "website/docs/r/virtual_network_peering.html.markdown", + "slug": "virtual_network_peering", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27924", + "title": "virtual_wan", + "path": "website/docs/r/virtual_wan.html.markdown", + "slug": "virtual_wan", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27925", + "title": "web_application_firewall_policy", + "path": "website/docs/r/web_application_firewall_policy.html.markdown", + "slug": "web_application_firewall_policy", + "category": "resources", + "subcategory": "Network" + }, + { + "id": "27926", + "title": "windows_virtual_machine_scale_set", + "path": "website/docs/r/windows_virtual_machine_scale_set.html.markdown", + "slug": "windows_virtual_machine_scale_set", + "category": "resources", + "subcategory": "Beta" + } + ] +} diff --git a/lib/datasource/terraform-provider/__fixtures__/releaseBackendIndex.json b/lib/datasource/terraform-provider/__fixtures__/releaseBackendIndex.json index a5cd75cb8425e8..ff04c0cd8ebf9e 100644 --- a/lib/datasource/terraform-provider/__fixtures__/releaseBackendIndex.json +++ b/lib/datasource/terraform-provider/__fixtures__/releaseBackendIndex.json @@ -43,26 +43,26 @@ { "name": "terraform-provider-google-beta", "version": "1.20.0", - "os": "openbsd", - "arch": "386", - "filename": "terraform-provider-google-beta_1.20.0_openbsd_386.zip", - "url": "https://releases.hashicorp.com/terraform-provider-google-beta/1.20.0/terraform-provider-google-beta_1.20.0_openbsd_386.zip" + "os": "darwin", + "arch": "amd64", + "filename": "terraform-provider-google-beta_1.20.0_darwin_amd64.zip", + "url": "https://releases.hashicorp.com/terraform-provider-google-beta/1.20.0/terraform-provider-google-beta_1.20.0_darwin_amd64.zip" }, { "name": "terraform-provider-google-beta", "version": "1.20.0", - "os": "openbsd", - "arch": "amd64", - "filename": "terraform-provider-google-beta_1.20.0_openbsd_amd64.zip", - "url": "https://releases.hashicorp.com/terraform-provider-google-beta/1.20.0/terraform-provider-google-beta_1.20.0_openbsd_amd64.zip" + "os": "freebsd", + "arch": "386", + "filename": "terraform-provider-google-beta_1.20.0_freebsd_386.zip", + "url": "https://releases.hashicorp.com/terraform-provider-google-beta/1.20.0/terraform-provider-google-beta_1.20.0_freebsd_386.zip" }, { "name": "terraform-provider-google-beta", "version": "1.20.0", - "os": "solaris", + "os": "freebsd", "arch": "amd64", - "filename": "terraform-provider-google-beta_1.20.0_solaris_amd64.zip", - "url": "https://releases.hashicorp.com/terraform-provider-google-beta/1.20.0/terraform-provider-google-beta_1.20.0_solaris_amd64.zip" + "filename": "terraform-provider-google-beta_1.20.0_freebsd_amd64.zip", + "url": "https://releases.hashicorp.com/terraform-provider-google-beta/1.20.0/terraform-provider-google-beta_1.20.0_freebsd_amd64.zip" } ] }, diff --git a/lib/datasource/terraform-provider/__snapshots__/index.spec.ts.snap b/lib/datasource/terraform-provider/__snapshots__/index.spec.ts.snap index 600ed430ee6c80..1790d5fed3a454 100644 --- a/lib/datasource/terraform-provider/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/terraform-provider/__snapshots__/index.spec.ts.snap @@ -59,200 +59,11 @@ Object { "registryUrl": "https://registry.terraform.io", "releases": Array [ Object { - "version": "0.1.0", - }, - Object { - "version": "0.1.1", - }, - Object { - "version": "0.1.2", - }, - Object { - "version": "0.1.3", - }, - Object { - "version": "0.1.4", - }, - Object { - "version": "0.1.5", - }, - Object { - "version": "0.1.6", - }, - Object { - "version": "0.1.7", - }, - Object { - "version": "0.2.0", - }, - Object { - "version": "0.2.1", - }, - Object { - "version": "0.2.2", - }, - Object { - "version": "0.3.0", - }, - Object { - "version": "0.3.1", - }, - Object { - "version": "0.3.2", - }, - Object { - "version": "0.3.3", - }, - Object { - "version": "1.0.0", - }, - Object { - "version": "1.0.1", - }, - Object { - "version": "1.1.0", - }, - Object { - "version": "1.1.1", - }, - Object { - "version": "1.1.2", - }, - Object { - "version": "1.2.0", - }, - Object { - "version": "1.3.0", - }, - Object { - "version": "1.3.1", - }, - Object { - "version": "1.3.2", - }, - Object { - "version": "1.3.3", - }, - Object { - "version": "1.4.0", - }, - Object { - "version": "1.5.0", - }, - Object { - "version": "1.6.0", - }, - Object { - "version": "1.7.0", - }, - Object { - "version": "1.8.0", - }, - Object { - "version": "1.9.0", - }, - Object { - "version": "1.10.0", - }, - Object { - "version": "1.11.0", - }, - Object { - "version": "1.12.0", - }, - Object { - "version": "1.13.0", - }, - Object { - "version": "1.14.0", - }, - Object { - "version": "1.15.0", - }, - Object { - "version": "1.16.0", - }, - Object { - "version": "1.17.0", - }, - Object { - "version": "1.18.0", - }, - Object { - "version": "1.19.0", - }, - Object { - "version": "1.20.0", - }, - Object { - "version": "1.21.0", - }, - Object { - "version": "1.22.0", - }, - Object { - "version": "1.22.1", - }, - Object { - "version": "1.23.0", - }, - Object { - "version": "1.24.0", - }, - Object { - "version": "1.25.0", - }, - Object { - "version": "1.26.0", - }, - Object { - "version": "1.27.0", - }, - Object { - "version": "1.27.1", - }, - Object { - "version": "1.28.0", - }, - Object { - "version": "1.29.0", - }, - Object { - "version": "1.30.0", - }, - Object { - "version": "1.30.1", - }, - Object { - "version": "1.31.0", - }, - Object { - "version": "1.32.0", - }, - Object { - "version": "1.32.1", - }, - Object { - "version": "1.33.0", - }, - Object { - "version": "1.33.1", - }, - Object { - "version": "1.34.0", - }, - Object { - "version": "1.35.0", - }, - Object { - "version": "1.36.0", - }, - Object { - "version": "1.36.1", + "version": "2.52.0", }, Object { "releaseTimestamp": "2019-11-26T08:22:56.000Z", - "version": "1.37.0", + "version": "2.53.0", }, ], "sourceUrl": "https://github.com/terraform-providers/terraform-provider-azurerm", @@ -290,200 +101,11 @@ Object { "registryUrl": "https://registry.company.com", "releases": Array [ Object { - "version": "0.1.0", - }, - Object { - "version": "0.1.1", - }, - Object { - "version": "0.1.2", - }, - Object { - "version": "0.1.3", - }, - Object { - "version": "0.1.4", - }, - Object { - "version": "0.1.5", - }, - Object { - "version": "0.1.6", - }, - Object { - "version": "0.1.7", - }, - Object { - "version": "0.2.0", - }, - Object { - "version": "0.2.1", - }, - Object { - "version": "0.2.2", - }, - Object { - "version": "0.3.0", - }, - Object { - "version": "0.3.1", - }, - Object { - "version": "0.3.2", - }, - Object { - "version": "0.3.3", - }, - Object { - "version": "1.0.0", - }, - Object { - "version": "1.0.1", - }, - Object { - "version": "1.1.0", - }, - Object { - "version": "1.1.1", - }, - Object { - "version": "1.1.2", - }, - Object { - "version": "1.2.0", - }, - Object { - "version": "1.3.0", - }, - Object { - "version": "1.3.1", - }, - Object { - "version": "1.3.2", - }, - Object { - "version": "1.3.3", - }, - Object { - "version": "1.4.0", - }, - Object { - "version": "1.5.0", - }, - Object { - "version": "1.6.0", - }, - Object { - "version": "1.7.0", - }, - Object { - "version": "1.8.0", - }, - Object { - "version": "1.9.0", - }, - Object { - "version": "1.10.0", - }, - Object { - "version": "1.11.0", - }, - Object { - "version": "1.12.0", - }, - Object { - "version": "1.13.0", - }, - Object { - "version": "1.14.0", - }, - Object { - "version": "1.15.0", - }, - Object { - "version": "1.16.0", - }, - Object { - "version": "1.17.0", - }, - Object { - "version": "1.18.0", - }, - Object { - "version": "1.19.0", - }, - Object { - "version": "1.20.0", - }, - Object { - "version": "1.21.0", - }, - Object { - "version": "1.22.0", - }, - Object { - "version": "1.22.1", - }, - Object { - "version": "1.23.0", - }, - Object { - "version": "1.24.0", - }, - Object { - "version": "1.25.0", - }, - Object { - "version": "1.26.0", - }, - Object { - "version": "1.27.0", - }, - Object { - "version": "1.27.1", - }, - Object { - "version": "1.28.0", - }, - Object { - "version": "1.29.0", - }, - Object { - "version": "1.30.0", - }, - Object { - "version": "1.30.1", - }, - Object { - "version": "1.31.0", - }, - Object { - "version": "1.32.0", - }, - Object { - "version": "1.32.1", - }, - Object { - "version": "1.33.0", - }, - Object { - "version": "1.33.1", - }, - Object { - "version": "1.34.0", - }, - Object { - "version": "1.35.0", - }, - Object { - "version": "1.36.0", - }, - Object { - "version": "1.36.1", + "version": "2.52.0", }, Object { "releaseTimestamp": "2019-11-26T08:22:56.000Z", - "version": "1.37.0", + "version": "2.53.0", }, ], "sourceUrl": "https://github.com/terraform-providers/terraform-provider-azurerm", @@ -646,3 +268,38 @@ Array [ `; exports[`datasource/terraform-provider/index getReleases simulate failing secondary release source 1`] = `null`; + +exports[`datasource/terraform-provider/index getReleases simulate failing secondary release source 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "registry.terraform.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.terraform.io/.well-known/terraform.json", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "registry.terraform.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.terraform.io/v1/providers/hashicorp/datadog", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "releases.hashicorp.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://releases.hashicorp.com/index.json", + }, +] +`; diff --git a/lib/datasource/terraform-provider/index.spec.ts b/lib/datasource/terraform-provider/index.spec.ts index 10a5abb85e8c55..afde1cffb3924b 100644 --- a/lib/datasource/terraform-provider/index.spec.ts +++ b/lib/datasource/terraform-provider/index.spec.ts @@ -1,24 +1,20 @@ import { getPkgReleases } from '..'; import * as httpMock from '../../../test/http-mock'; import { getName, loadFixture } from '../../../test/util'; -import { id as datasource, defaultRegistryUrls } from '.'; +import { TerraformProviderDatasource } from '.'; const consulData: any = loadFixture('azurerm-provider.json'); const hashicorpReleases: any = loadFixture('releaseBackendIndex.json'); const serviceDiscoveryResult: any = loadFixture('service-discovery.json'); -const primaryUrl = defaultRegistryUrls[0]; -const secondaryUrl = defaultRegistryUrls[1]; +const terraformProviderDatasource = new TerraformProviderDatasource(); +const primaryUrl = terraformProviderDatasource.defaultRegistryUrls[0]; +const secondaryUrl = terraformProviderDatasource.defaultRegistryUrls[1]; describe(getName(), () => { describe('getReleases', () => { beforeEach(() => { jest.clearAllMocks(); - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); }); it('returns null for empty result', async () => { @@ -31,7 +27,7 @@ describe(getName(), () => { httpMock.scope(secondaryUrl).get('/index.json').reply(200, {}); expect( await getPkgReleases({ - datasource, + datasource: TerraformProviderDatasource.id, depName: 'azurerm', }) ).toBeNull(); @@ -47,7 +43,7 @@ describe(getName(), () => { httpMock.scope(secondaryUrl).get('/index.json').reply(404); expect( await getPkgReleases({ - datasource, + datasource: TerraformProviderDatasource.id, depName: 'azurerm', }) ).toBeNull(); @@ -63,7 +59,7 @@ describe(getName(), () => { httpMock.scope(secondaryUrl).get('/index.json').replyWithError(''); expect( await getPkgReleases({ - datasource, + datasource: TerraformProviderDatasource.id, depName: 'azurerm', }) ).toBeNull(); @@ -77,7 +73,7 @@ describe(getName(), () => { .get('/.well-known/terraform.json') .reply(200, serviceDiscoveryResult); const res = await getPkgReleases({ - datasource, + datasource: TerraformProviderDatasource.id, depName: 'azurerm', }); expect(res).toMatchSnapshot(); @@ -93,7 +89,7 @@ describe(getName(), () => { .get('/.well-known/terraform.json') .reply(200, serviceDiscoveryResult); const res = await getPkgReleases({ - datasource, + datasource: TerraformProviderDatasource.id, depName: 'azure', lookupName: 'hashicorp/azurerm', registryUrls: ['https://registry.company.com'], @@ -117,7 +113,7 @@ describe(getName(), () => { .reply(200, JSON.parse(hashicorpReleases)); const res = await getPkgReleases({ - datasource, + datasource: TerraformProviderDatasource.id, depName: 'google-beta', }); expect(res).toMatchSnapshot(); @@ -127,7 +123,7 @@ describe(getName(), () => { it('simulate failing secondary release source', async () => { httpMock .scope(primaryUrl) - .get('/v1/providers/hashicorp/google-beta') + .get('/v1/providers/hashicorp/datadog') .reply(404, { errors: ['Not Found'], }) @@ -136,18 +132,19 @@ describe(getName(), () => { httpMock.scope(secondaryUrl).get('/index.json').reply(404); const res = await getPkgReleases({ - datasource, + datasource: TerraformProviderDatasource.id, depName: 'datadog', }); expect(res).toMatchSnapshot(); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for error in service discovery', async () => { httpMock.scope(primaryUrl).get('/.well-known/terraform.json').reply(404); httpMock.scope(secondaryUrl).get('/index.json').replyWithError(''); expect( await getPkgReleases({ - datasource, + datasource: TerraformProviderDatasource.id, depName: 'azurerm', }) ).toBeNull(); diff --git a/lib/datasource/terraform-provider/index.ts b/lib/datasource/terraform-provider/index.ts index 10986fedbc7e77..07e401b8f7ab72 100644 --- a/lib/datasource/terraform-provider/index.ts +++ b/lib/datasource/terraform-provider/index.ts @@ -1,8 +1,8 @@ -import URL from 'url'; import { logger } from '../../logger'; -import * as packageCache from '../../util/cache/package'; -import { Http } from '../../util/http'; +import { cache } from '../../util/cache/package/decorator'; +import { parseUrl } from '../../util/url'; import * as hashicorpVersioning from '../../versioning/hashicorp'; +import { Datasource } from '../datasource'; import { getTerraformServiceDiscoveryResult } from '../terraform-module'; import type { GetReleasesConfig, ReleaseResult } from '../types'; import type { @@ -10,110 +10,111 @@ import type { TerraformProviderReleaseBackend, } from './types'; -export const id = 'terraform-provider'; -export const customRegistrySupport = true; -export const defaultRegistryUrls = [ - 'https://registry.terraform.io', - 'https://releases.hashicorp.com', -]; -export const defaultVersioning = hashicorpVersioning.id; -export const registryStrategy = 'hunt'; +export class TerraformProviderDatasource extends Datasource { + static readonly id = 'terraform-provider'; -const http = new Http(id); + static readonly defaultRegistryUrls = [ + 'https://registry.terraform.io', + 'https://releases.hashicorp.com', + ]; -async function queryRegistry( - lookupName: string, - registryURL: string, - repository: string -): Promise { - const serviceDiscovery = await getTerraformServiceDiscoveryResult( - registryURL - ); - const backendURL = `${registryURL}${serviceDiscovery['providers.v1']}${repository}`; - const res = (await http.getJson(backendURL)).body; - const dep: ReleaseResult = { - releases: null, - }; - if (res.source) { - dep.sourceUrl = res.source; + constructor() { + super(TerraformProviderDatasource.id); } - dep.releases = res.versions.map((version) => ({ - version, - })); - // set published date for latest release - const latestVersion = dep.releases.find( - (release) => res.version === release.version - ); - // istanbul ignore else - if (latestVersion) { - latestVersion.releaseTimestamp = res.published_at; - } - dep.homepage = `${registryURL}/providers/${repository}`; - logger.trace({ dep }, 'dep'); - return dep; -} -// TODO: add long term cache (#9590) -async function queryReleaseBackend( - lookupName: string, - registryURL: string, - repository: string -): Promise { - const backendLookUpName = `terraform-provider-${lookupName}`; - const backendURL = registryURL + `/index.json`; - const res = (await http.getJson(backendURL)) - .body; + readonly defaultRegistryUrls = + TerraformProviderDatasource.defaultRegistryUrls; + + readonly defaultVersioning = hashicorpVersioning.id; + + readonly registryStrategy = 'hunt'; + + @cache({ + namespace: `datasource-${TerraformProviderDatasource.id}`, + key: (getReleasesConfig: GetReleasesConfig) => + `${ + getReleasesConfig.registryUrl + }/${TerraformProviderDatasource.getRepository(getReleasesConfig)}`, + }) + async getReleases({ + lookupName, + registryUrl, + }: GetReleasesConfig): Promise { + logger.debug({ lookupName }, 'terraform-provider.getDependencies()'); + let dep: ReleaseResult = null; + const registryHost = parseUrl(registryUrl).host; + if (registryHost === 'releases.hashicorp.com') { + dep = await this.queryReleaseBackend(lookupName, registryUrl); + } else { + const repository = TerraformProviderDatasource.getRepository({ + lookupName, + }); + dep = await this.queryRegistry(registryUrl, repository); + } - if (!res[backendLookUpName]) { - return null; + return dep; } - const dep: ReleaseResult = { - releases: null, - sourceUrl: `https://github.com/terraform-providers/${backendLookUpName}`, - }; - dep.releases = Object.keys(res[backendLookUpName].versions).map( - (version) => ({ + private static getRepository({ lookupName }: GetReleasesConfig): string { + return lookupName.includes('/') ? lookupName : `hashicorp/${lookupName}`; + } + + private async queryRegistry( + registryURL: string, + repository: string + ): Promise { + const serviceDiscovery = await getTerraformServiceDiscoveryResult( + registryURL + ); + const backendURL = `${registryURL}${serviceDiscovery['providers.v1']}${repository}`; + const res = (await this.http.getJson(backendURL)).body; + const dep: ReleaseResult = { + releases: null, + }; + if (res.source) { + dep.sourceUrl = res.source; + } + dep.releases = res.versions.map((version) => ({ version, - }) - ); - logger.trace({ dep }, 'dep'); - return dep; -} + })); + // set published date for latest release + const latestVersion = dep.releases.find( + (release) => res.version === release.version + ); + // istanbul ignore else + if (latestVersion) { + latestVersion.releaseTimestamp = res.published_at; + } + dep.homepage = `${registryURL}/providers/${repository}`; + logger.trace({ dep }, 'dep'); + return dep; + } -/** - * terraform-provider.getReleases - * - * This function will fetch a provider from the public Terraform registry and return all semver versions. - */ -export async function getReleases({ - lookupName, - registryUrl, -}: GetReleasesConfig): Promise { - const repository = lookupName.includes('/') - ? lookupName - : `hashicorp/${lookupName}`; + // TODO: add long term cache (#9590) + private async queryReleaseBackend( + lookupName: string, + registryURL: string + ): Promise { + const backendLookUpName = `terraform-provider-${lookupName}`; + const backendURL = registryURL + `/index.json`; + const res = ( + await this.http.getJson(backendURL) + ).body; - const cacheNamespace = 'terraform-provider'; - const pkgUrl = `${registryUrl}/${repository}`; - const cachedResult = await packageCache.get( - cacheNamespace, - pkgUrl - ); - // istanbul ignore if - if (cachedResult) { - return cachedResult; - } + if (!res[backendLookUpName]) { + return null; + } - logger.debug({ lookupName }, 'terraform-provider.getDependencies()'); - let dep: ReleaseResult = null; - const registryHost = URL.parse(registryUrl).host; - if (registryHost === 'releases.hashicorp.com') { - dep = await queryReleaseBackend(lookupName, registryUrl, repository); - } else { - dep = await queryRegistry(lookupName, registryUrl, repository); + const dep: ReleaseResult = { + releases: null, + sourceUrl: `https://github.com/terraform-providers/${backendLookUpName}`, + }; + dep.releases = Object.keys(res[backendLookUpName].versions).map( + (version) => ({ + version, + }) + ); + logger.trace({ dep }, 'dep'); + return dep; } - const cacheMinutes = 30; - await packageCache.set(cacheNamespace, pkgUrl, dep, cacheMinutes); - return dep; } diff --git a/lib/datasource/terraform-provider/types.ts b/lib/datasource/terraform-provider/types.ts index 40f3bdfadc50ce..e2a739c48da723 100644 --- a/lib/datasource/terraform-provider/types.ts +++ b/lib/datasource/terraform-provider/types.ts @@ -1,3 +1,18 @@ +export interface VersionDetailResponse { + name: string; + version: string; + builds: TerraformBuild[]; +} + +export interface TerraformBuild { + name: string; + version: string; + os: string; + arch: string; + filename: string; + url: string; +} + export interface TerraformProvider { namespace: string; name: string; @@ -8,13 +23,12 @@ export interface TerraformProvider { published_at: string; } -export interface TerraformProviderReleaseBackend { - [key: string]: { +export type TerraformProviderReleaseBackend = Record< + string, + { name: string; versions: VersionsReleaseBackend; - }; -} + } +>; -interface VersionsReleaseBackend { - [key: string]: Record; -} +export type VersionsReleaseBackend = Record; diff --git a/lib/logger/err-serializer.spec.ts b/lib/logger/err-serializer.spec.ts index afb2d1d65d8ba1..7864faff5135ca 100644 --- a/lib/logger/err-serializer.spec.ts +++ b/lib/logger/err-serializer.spec.ts @@ -38,7 +38,6 @@ describe(getName(), () => { beforeEach(() => { // reset module jest.resetAllMocks(); - httpMock.setup(); // clean up hostRules hostRules.clear(); hostRules.add({ @@ -47,7 +46,6 @@ describe(getName(), () => { token: 'token', }); }); - afterEach(() => httpMock.reset()); it('handles http error', async () => { httpMock diff --git a/lib/manager/ansible-galaxy/collections-metadata.ts b/lib/manager/ansible-galaxy/collections-metadata.ts index 8c41b27d0a47ee..e91b21523f74fe 100644 --- a/lib/manager/ansible-galaxy/collections-metadata.ts +++ b/lib/manager/ansible-galaxy/collections-metadata.ts @@ -1,4 +1,4 @@ -import * as datasourceGalaxyCollection from '../../datasource/galaxy-collection'; +import { GalaxyCollectionDatasource } from '../../datasource/galaxy-collection'; import type { PackageDependency } from '../types'; import { dependencyRegex, galaxyRegEx } from './util'; @@ -19,7 +19,7 @@ export function extractCollectionsMetaDataFile( if (galaxyRegExResult) { const dep: PackageDependency = { depType: 'galaxy-collection', - datasource: datasourceGalaxyCollection.id, + datasource: GalaxyCollectionDatasource.id, depName: galaxyRegExResult.groups.lookupName, currentValue: galaxyRegExResult.groups.version, }; diff --git a/lib/manager/ansible-galaxy/collections.ts b/lib/manager/ansible-galaxy/collections.ts index ec9584d8de496b..87072e3e3461fe 100644 --- a/lib/manager/ansible-galaxy/collections.ts +++ b/lib/manager/ansible-galaxy/collections.ts @@ -1,4 +1,4 @@ -import * as datasourceGalaxyCollection from '../../datasource/galaxy-collection'; +import { GalaxyCollectionDatasource } from '../../datasource/galaxy-collection'; import * as datasourceGitTags from '../../datasource/git-tags'; import * as datasourceGithubTags from '../../datasource/github-tags'; import { SkipReason } from '../../types'; @@ -77,7 +77,7 @@ function handleGitDep( function handleGalaxyDep(dep: PackageDependency): void { /* eslint-disable no-param-reassign */ - dep.datasource = datasourceGalaxyCollection.id; + dep.datasource = GalaxyCollectionDatasource.id; dep.depName = dep.managerData.name; dep.registryUrls = dep.managerData.source ? [dep.managerData.source] : []; dep.currentValue = dep.managerData.version; @@ -109,7 +109,7 @@ function finalize(dependency: PackageDependency): boolean { break; } if (galaxyDepRegex.exec(dep.managerData.name)) { - dep.datasource = datasourceGalaxyCollection.id; + dep.datasource = GalaxyCollectionDatasource.id; dep.depName = dep.managerData.name; break; } diff --git a/lib/manager/ansible-galaxy/roles.ts b/lib/manager/ansible-galaxy/roles.ts index 765203c58917ef..a1e73e548dfa0a 100644 --- a/lib/manager/ansible-galaxy/roles.ts +++ b/lib/manager/ansible-galaxy/roles.ts @@ -1,4 +1,4 @@ -import * as datasourceGalaxy from '../../datasource/galaxy'; +import { GalaxyDatasource } from '../../datasource/galaxy'; import * as datasourceGitTags from '../../datasource/git-tags'; import { SkipReason } from '../../types'; import type { PackageDependency } from '../types'; @@ -58,11 +58,11 @@ function finalize(dependency: PackageDependency): boolean { // remove leading `git+` from URLs like `git+https://...` dep.lookupName = source.replace(/git\+/, ''); } else if (galaxyDepRegex.exec(source)) { - dep.datasource = datasourceGalaxy.id; + dep.datasource = GalaxyDatasource.id; dep.depName = dep.managerData.src; dep.lookupName = dep.managerData.src; } else if (galaxyDepRegex.exec(dep.managerData.name)) { - dep.datasource = datasourceGalaxy.id; + dep.datasource = GalaxyDatasource.id; dep.depName = dep.managerData.name; dep.lookupName = dep.managerData.name; } else { diff --git a/lib/manager/api.ts b/lib/manager/api.ts index 27ddd92efaf2f0..f6e05be9c58729 100644 --- a/lib/manager/api.ts +++ b/lib/manager/api.ts @@ -43,12 +43,14 @@ import * as nodenv from './nodenv'; import * as npm from './npm'; import * as nuget from './nuget'; import * as nvm from './nvm'; +import * as pipCompile from './pip-compile'; import * as pip_requirements from './pip_requirements'; import * as pip_setup from './pip_setup'; import * as pipenv from './pipenv'; import * as poetry from './poetry'; import * as preCommit from './pre-commit'; import * as pub from './pub'; +import * as pyenv from './pyenv'; import * as regex from './regex'; import * as rubyVersion from './ruby-version'; import * as sbt from './sbt'; @@ -109,12 +111,14 @@ api.set('nodenv', nodenv); api.set('npm', npm); api.set('nuget', nuget); api.set('nvm', nvm); +api.set('pip-compile', pipCompile); api.set('pip_requirements', pip_requirements); api.set('pip_setup', pip_setup); api.set('pipenv', pipenv); api.set('poetry', poetry); api.set('pre-commit', preCommit); api.set('pub', pub); +api.set('pyenv', pyenv); api.set('regex', regex); api.set('ruby-version', rubyVersion); api.set('sbt', sbt); diff --git a/lib/manager/argocd/extract.ts b/lib/manager/argocd/extract.ts index 40083281be1721..7de4ca1ece2c84 100644 --- a/lib/manager/argocd/extract.ts +++ b/lib/manager/argocd/extract.ts @@ -1,6 +1,6 @@ import { loadAll } from 'js-yaml'; import * as gitTags from '../../datasource/git-tags'; -import * as helm from '../../datasource/helm'; +import { HelmDatasource } from '../../datasource/helm'; import type { ExtractConfig, PackageDependency, PackageFile } from '../types'; import type { ApplicationDefinition } from './types'; import { fileTestRegex } from './util'; @@ -20,7 +20,7 @@ function createDependency( depName: source.chart, registryUrls: [source.repoURL], currentValue: source.targetRevision, - datasource: helm.id, + datasource: HelmDatasource.id, }; } return { diff --git a/lib/manager/batect-wrapper/artifacts.spec.ts b/lib/manager/batect-wrapper/artifacts.spec.ts index 1d14f7b76fd1aa..94407ba1862cb4 100644 --- a/lib/manager/batect-wrapper/artifacts.spec.ts +++ b/lib/manager/batect-wrapper/artifacts.spec.ts @@ -23,8 +23,6 @@ function artifactForPath( describe(getName(), () => { beforeEach(() => { - httpMock.setup(); - httpMock .scope('https://github.com') .get('/batect/batect/releases/download/1.2.3/batect') @@ -46,9 +44,8 @@ describe(getName(), () => { .reply(418); }); - afterEach(() => { - httpMock.reset(); - }); + // TODO: fix mocks + afterEach(() => httpMock.clear(false)); describe('updateArtifacts', () => { it('returns updated files if the wrapper script is in the root directory', async () => { diff --git a/lib/manager/bazel/update.spec.ts b/lib/manager/bazel/update.spec.ts index 368c092205f325..3be8723119eef1 100644 --- a/lib/manager/bazel/update.spec.ts +++ b/lib/manager/bazel/update.spec.ts @@ -20,11 +20,6 @@ describe(getName(), () => { describe('updateDependency', () => { beforeEach(() => { jest.resetAllMocks(); - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); }); it('updates tag', async () => { diff --git a/lib/manager/bundler/artifacts.spec.ts b/lib/manager/bundler/artifacts.spec.ts index 05cf2d6ca7c56e..f9abf89ec00784 100644 --- a/lib/manager/bundler/artifacts.spec.ts +++ b/lib/manager/bundler/artifacts.spec.ts @@ -5,8 +5,6 @@ import { fs, git, mocked } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; import * as _datasource from '../../datasource'; -import { setExecConfig } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import * as docker from '../../util/exec/docker'; import * as _env from '../../util/exec/env'; import type { StatusResult } from '../../util/git'; @@ -37,7 +35,7 @@ const adminConfig: RepoAdminConfig = { const config: UpdateArtifactsConfig = {}; describe('bundler.updateArtifacts()', () => { - beforeEach(async () => { + beforeEach(() => { jest.resetAllMocks(); jest.resetModules(); @@ -47,7 +45,6 @@ describe('bundler.updateArtifacts()', () => { bundlerHostRules.findAllAuthenticatable.mockReturnValue([]); docker.resetPrefetchedImages(); - await setExecConfig(adminConfig as never); setAdminConfig(adminConfig); }); afterEach(() => { @@ -101,6 +98,7 @@ describe('bundler.updateArtifacts()', () => { expect(execSnapshots).toMatchSnapshot(); }); it('works explicit global binarySource', async () => { + setAdminConfig({ ...adminConfig, binarySource: 'global' }); fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock'); fs.writeLocalFile.mockResolvedValueOnce(null as never); fs.readLocalFile.mockResolvedValueOnce(null); @@ -114,20 +112,16 @@ describe('bundler.updateArtifacts()', () => { packageFileName: 'Gemfile', updatedDeps: ['foo', 'bar'], newPackageFileContent: 'Updated Gemfile content', - config: { - ...config, - binarySource: BinarySource.Global, - }, + config, }) ).toMatchSnapshot(); expect(execSnapshots).toMatchSnapshot(); }); describe('Docker', () => { - beforeEach(async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig({ + beforeEach(() => { + setAdminConfig({ ...adminConfig, - binarySource: BinarySource.Docker, + binarySource: 'docker', }); }); it('.ruby-version', async () => { @@ -151,15 +145,13 @@ describe('bundler.updateArtifacts()', () => { packageFileName: 'Gemfile', updatedDeps: ['foo', 'bar'], newPackageFileContent: 'Updated Gemfile content', - config: { - ...config, - binarySource: BinarySource.Docker, - }, + config, }) ).toMatchSnapshot(); expect(execSnapshots).toMatchSnapshot(); }); it('constraints options', async () => { + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock'); fs.writeLocalFile.mockResolvedValueOnce(null as never); datasource.getPkgReleases.mockResolvedValueOnce({ @@ -181,7 +173,6 @@ describe('bundler.updateArtifacts()', () => { newPackageFileContent: 'Updated Gemfile content', config: { ...config, - binarySource: BinarySource.Docker, constraints: { ruby: '1.2.5', bundler: '3.2.1', @@ -192,6 +183,7 @@ describe('bundler.updateArtifacts()', () => { expect(execSnapshots).toMatchSnapshot(); }); it('invalid constraints options', async () => { + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock'); fs.writeLocalFile.mockResolvedValueOnce(null as never); datasource.getPkgReleases.mockResolvedValueOnce({ @@ -213,7 +205,6 @@ describe('bundler.updateArtifacts()', () => { newPackageFileContent: 'Updated Gemfile content', config: { ...config, - binarySource: BinarySource.Docker, constraints: { ruby: 'foo', bundler: 'bar', @@ -225,6 +216,7 @@ describe('bundler.updateArtifacts()', () => { }); it('injects bundler host configuration environment variables', async () => { + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock'); fs.writeLocalFile.mockResolvedValueOnce(null as never); fs.readLocalFile.mockResolvedValueOnce('1.2.0'); @@ -257,16 +249,14 @@ describe('bundler.updateArtifacts()', () => { packageFileName: 'Gemfile', updatedDeps: ['foo', 'bar'], newPackageFileContent: 'Updated Gemfile content', - config: { - ...config, - binarySource: BinarySource.Docker, - }, + config, }) ).toMatchSnapshot(); expect(execSnapshots).toMatchSnapshot(); }); it('injects bundler host configuration as command with bundler < 2', async () => { + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock'); fs.writeLocalFile.mockResolvedValueOnce(null as never); fs.readLocalFile.mockResolvedValueOnce('1.2.0'); @@ -301,7 +291,6 @@ describe('bundler.updateArtifacts()', () => { newPackageFileContent: 'Updated Gemfile content', config: { ...config, - binarySource: BinarySource.Docker, constraints: { bundler: '1.2', }, @@ -312,6 +301,7 @@ describe('bundler.updateArtifacts()', () => { }); it('injects bundler host configuration as command with bundler >= 2', async () => { + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock'); fs.writeLocalFile.mockResolvedValueOnce(null as never); fs.readLocalFile.mockResolvedValueOnce('1.2.0'); @@ -346,7 +336,6 @@ describe('bundler.updateArtifacts()', () => { newPackageFileContent: 'Updated Gemfile content', config: { ...config, - binarySource: BinarySource.Docker, constraints: { bundler: '2.1', }, @@ -357,6 +346,7 @@ describe('bundler.updateArtifacts()', () => { }); it('injects bundler host configuration as command with bundler == latest', async () => { + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock'); fs.writeLocalFile.mockResolvedValueOnce(null as never); fs.readLocalFile.mockResolvedValueOnce('1.2.0'); @@ -389,10 +379,7 @@ describe('bundler.updateArtifacts()', () => { packageFileName: 'Gemfile', updatedDeps: ['foo', 'bar'], newPackageFileContent: 'Updated Gemfile content', - config: { - ...config, - binarySource: BinarySource.Docker, - }, + config, }) ).toMatchSnapshot(); expect(execSnapshots).toMatchSnapshot(); diff --git a/lib/manager/cake/__fixtures__/build.cake b/lib/manager/cake/__fixtures__/build.cake index f3d942b91997e7..c678319560c538 100644 --- a/lib/manager/cake/__fixtures__/build.cake +++ b/lib/manager/cake/__fixtures__/build.cake @@ -3,6 +3,8 @@ foo #addin "nuget:?package=Bim.Bim&version=6.6.6" #tool nuget:https://example.com?package=Bar.Bar&version=2.2.2 #module nuget:file:///tmp/?package=Baz.Baz&version=3.3.3 +#load nuget:?package=Cake.7zip&version=1.0.3 +#l nuget:?package=Cake.asciidoctorj&version=1.0.0 // #module nuget:?package=Qux.Qux&version=4.4.4 /* #module nuget:?package=Quux.Quux&version=5.5.5 diff --git a/lib/manager/cake/__snapshots__/index.spec.ts.snap b/lib/manager/cake/__snapshots__/index.spec.ts.snap index 8df0548df51e2a..aa18c9baff6230 100644 --- a/lib/manager/cake/__snapshots__/index.spec.ts.snap +++ b/lib/manager/cake/__snapshots__/index.spec.ts.snap @@ -27,6 +27,16 @@ Object { "depName": "Baz.Baz", "skipReason": "unsupported-url", }, + Object { + "currentValue": "1.0.3", + "datasource": "nuget", + "depName": "Cake.7zip", + }, + Object { + "currentValue": "1.0.0", + "datasource": "nuget", + "depName": "Cake.asciidoctorj", + }, ], } `; diff --git a/lib/manager/cake/index.ts b/lib/manager/cake/index.ts index f7352cb1f54eae..77e334e2f043e8 100644 --- a/lib/manager/cake/index.ts +++ b/lib/manager/cake/index.ts @@ -15,10 +15,10 @@ const lexer = moo.states({ lineComment: { match: /\/\/.*?$/ }, multiLineComment: { match: /\/\*[^]*?\*\//, lineBreaks: true }, dependency: { - match: /^#(?:addin|tool|module)\s+(?:nuget|dotnet):.*$/, + match: /^#(?:addin|tool|module|load|l)\s+(?:nuget|dotnet):.*$/, }, dependencyQuoted: { - match: /^#(?:addin|tool|module)\s+"(?:nuget|dotnet):[^"]+"\s*$/, + match: /^#(?:addin|tool|module|load|l)\s+"(?:nuget|dotnet):[^"]+"\s*$/, value: (s: string) => s.trim().slice(1, -1), }, unknown: moo.fallback, diff --git a/lib/manager/cargo/__snapshots__/extract.spec.ts.snap b/lib/manager/cargo/__snapshots__/extract.spec.ts.snap index 9a8d877e826b5c..edbf249c681323 100644 --- a/lib/manager/cargo/__snapshots__/extract.spec.ts.snap +++ b/lib/manager/cargo/__snapshots__/extract.spec.ts.snap @@ -391,6 +391,21 @@ Array [ ] `; +exports[`manager/cargo/extract extractPackageFile() extracts original package name of renamed dependencies 1`] = ` +Array [ + Object { + "currentValue": "0.4.0", + "datasource": "crate", + "depName": "boolector-solver", + "depType": "dependencies", + "lookupName": "boolector", + "managerData": Object { + "nestedVersion": true, + }, + }, +] +`; + exports[`manager/cargo/extract extractPackageFile() extracts platform specific dependencies 1`] = ` Array [ Object { diff --git a/lib/manager/cargo/artifacts.spec.ts b/lib/manager/cargo/artifacts.spec.ts index a2c8bf0fc32388..829f3be4b6cc32 100644 --- a/lib/manager/cargo/artifacts.spec.ts +++ b/lib/manager/cargo/artifacts.spec.ts @@ -5,8 +5,6 @@ import { envMock, mockExecAll } from '../../../test/exec-util'; import { git, mocked } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; -import { setExecConfig } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import * as docker from '../../util/exec/docker'; import * as _env from '../../util/exec/env'; import type { UpdateArtifactsConfig } from '../types'; @@ -30,12 +28,11 @@ const adminConfig: RepoAdminConfig = { }; describe('.updateArtifacts()', () => { - beforeEach(async () => { + beforeEach(() => { jest.resetAllMocks(); jest.resetModules(); env.getChildProcessEnv.mockReturnValue(envMock.basic); - await setExecConfig(adminConfig as never); setAdminConfig(adminConfig); docker.resetPrefetchedImages(); }); @@ -136,8 +133,7 @@ describe('.updateArtifacts()', () => { it('returns updated Cargo.lock with docker', async () => { fs.stat.mockResolvedValueOnce({ name: 'Cargo.lock' } as any); - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); git.getFile.mockResolvedValueOnce('Old Cargo.lock'); const execSnapshots = mockExecAll(exec); fs.readFile.mockResolvedValueOnce('New Cargo.lock' as any); diff --git a/lib/manager/cargo/extract.spec.ts b/lib/manager/cargo/extract.spec.ts index 25cd4087e2b9ee..d64737cf92e52c 100644 --- a/lib/manager/cargo/extract.spec.ts +++ b/lib/manager/cargo/extract.spec.ts @@ -122,5 +122,14 @@ describe(getName(), () => { expect(res.deps).toMatchSnapshot(); expect(res.deps).toHaveLength(3); }); + it('extracts original package name of renamed dependencies', async () => { + const cargotoml = + '[dependencies]\nboolector-solver = { package = "boolector", version = "0.4.0" }'; + const res = await extractPackageFile(cargotoml, 'Cargo.toml', config); + + expect(res.deps).toMatchSnapshot(); + expect(res.deps).toHaveLength(1); + expect(res.deps[0].lookupName).toEqual('boolector'); + }); }); }); diff --git a/lib/manager/cargo/extract.ts b/lib/manager/cargo/extract.ts index 5386c7cb002207..af76dedd3ac928 100644 --- a/lib/manager/cargo/extract.ts +++ b/lib/manager/cargo/extract.ts @@ -27,12 +27,16 @@ function extractFromSection( let currentValue = sectionContent[depName]; let nestedVersion = false; let registryUrls: string[] | undefined; + let lookupName: string | undefined; if (typeof currentValue !== 'string') { const version = currentValue.version; const path = currentValue.path; const git = currentValue.git; const registryName = currentValue.registry; + + lookupName = currentValue.package; + if (version) { currentValue = version; nestedVersion = true; @@ -77,6 +81,9 @@ function extractFromSection( if (target) { dep.target = target; } + if (lookupName) { + dep.lookupName = lookupName; + } deps.push(dep); }); return deps; diff --git a/lib/manager/cargo/types.ts b/lib/manager/cargo/types.ts index 27dc73f5ee7072..deb3189907d903 100644 --- a/lib/manager/cargo/types.ts +++ b/lib/manager/cargo/types.ts @@ -7,6 +7,8 @@ export interface CargoDep { version?: string; /** Name of a registry whose URL is configured in `.cargo/config.toml` */ registry?: string; + /** Name of a package to look up */ + package?: string; } export type CargoDeps = Record; diff --git a/lib/manager/circleci/extract.ts b/lib/manager/circleci/extract.ts index f71a48a3b7a36e..0ffe5ce1ea93b1 100644 --- a/lib/manager/circleci/extract.ts +++ b/lib/manager/circleci/extract.ts @@ -1,4 +1,4 @@ -import * as datasourceOrb from '../../datasource/orb'; +import { OrbDatasource } from '../../datasource/orb'; import { logger } from '../../logger'; import * as npmVersioning from '../../versioning/npm'; import { getDep } from '../dockerfile/extract'; @@ -36,7 +36,7 @@ export function extractPackageFile(content: string): PackageFile | null { depType: 'orb', depName, currentValue, - datasource: datasourceOrb.id, + datasource: OrbDatasource.id, lookupName: orbName, commitMessageTopic: '{{{depName}}} orb', versioning: npmVersioning.id, diff --git a/lib/manager/cocoapods/__snapshots__/artifacts.spec.ts.snap b/lib/manager/cocoapods/__snapshots__/artifacts.spec.ts.snap index a0579ac4f55f25..af1a2350d6120b 100644 --- a/lib/manager/cocoapods/__snapshots__/artifacts.spec.ts.snap +++ b/lib/manager/cocoapods/__snapshots__/artifacts.spec.ts.snap @@ -15,12 +15,6 @@ exports[`.updateArtifacts() catches write error 2`] = `Array []`; exports[`.updateArtifacts() dynamically selects Docker image tag 1`] = ` Array [ - Object { - "cmd": "docker ps --filter label=renovate_child -aq", - "options": Object { - "encoding": "utf-8", - }, - }, Object { "cmd": "docker pull renovate/cocoapods:1.2.4", "options": Object { @@ -57,12 +51,6 @@ Array [ exports[`.updateArtifacts() falls back to the \`latest\` Docker image tag 1`] = ` Array [ - Object { - "cmd": "docker ps --filter label=renovate_child -aq", - "options": Object { - "encoding": "utf-8", - }, - }, Object { "cmd": "docker pull renovate/cocoapods:latest", "options": Object { @@ -177,12 +165,6 @@ Array [ exports[`.updateArtifacts() returns updated Podfile 2`] = ` Array [ - Object { - "cmd": "docker ps --filter label=renovate_child -aq", - "options": Object { - "encoding": "utf-8", - }, - }, Object { "cmd": "docker pull renovate/cocoapods", "options": Object { @@ -248,12 +230,6 @@ Array [ exports[`.updateArtifacts() returns updated Podfile and Pods files 2`] = ` Array [ - Object { - "cmd": "docker ps --filter label=renovate_child -aq", - "options": Object { - "encoding": "utf-8", - }, - }, Object { "cmd": "docker ps --filter name=renovate_cocoapods -aq", "options": Object { diff --git a/lib/manager/cocoapods/artifacts.spec.ts b/lib/manager/cocoapods/artifacts.spec.ts index 2944f67e22553a..b3e4fc6d0fe55a 100644 --- a/lib/manager/cocoapods/artifacts.spec.ts +++ b/lib/manager/cocoapods/artifacts.spec.ts @@ -6,8 +6,6 @@ import { git, mocked } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; import * as _datasource from '../../datasource'; -import { setExecConfig } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import * as _env from '../../util/exec/env'; import type { StatusResult } from '../../util/git'; import type { UpdateArtifactsConfig } from '../types'; @@ -35,11 +33,10 @@ const adminConfig: RepoAdminConfig = { }; describe('.updateArtifacts()', () => { - beforeEach(async () => { + beforeEach(() => { jest.resetAllMocks(); env.getChildProcessEnv.mockReturnValue(envMock.basic); - await setExecConfig(adminConfig as never); setAdminConfig(adminConfig); datasource.getPkgReleases.mockResolvedValue({ @@ -127,7 +124,7 @@ describe('.updateArtifacts()', () => { }); it('returns updated Podfile', async () => { const execSnapshots = mockExecAll(exec); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readFile.mockResolvedValueOnce('Old Podfile' as any); git.getRepoStatus.mockResolvedValueOnce({ modified: ['Podfile.lock'], @@ -145,7 +142,7 @@ describe('.updateArtifacts()', () => { }); it('returns updated Podfile and Pods files', async () => { const execSnapshots = mockExecAll(exec); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readFile.mockResolvedValueOnce('Old Manifest.lock' as any); fs.readFile.mockResolvedValueOnce('New Podfile' as any); fs.readFile.mockResolvedValueOnce('Pods manifest' as any); @@ -198,7 +195,7 @@ describe('.updateArtifacts()', () => { it('dynamically selects Docker image tag', async () => { const execSnapshots = mockExecAll(exec); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readFile.mockResolvedValueOnce('COCOAPODS: 1.2.4' as any); @@ -219,7 +216,7 @@ describe('.updateArtifacts()', () => { it('falls back to the `latest` Docker image tag', async () => { const execSnapshots = mockExecAll(exec); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readFile.mockResolvedValueOnce('COCOAPODS: 1.2.4' as any); datasource.getPkgReleases.mockResolvedValueOnce({ diff --git a/lib/manager/composer/artifacts.spec.ts b/lib/manager/composer/artifacts.spec.ts index 23218407a9d28f..07f21e9dfa58d6 100644 --- a/lib/manager/composer/artifacts.spec.ts +++ b/lib/manager/composer/artifacts.spec.ts @@ -10,8 +10,6 @@ import { } from '../../constants/platforms'; import * as _datasource from '../../datasource'; import * as datasourcePackagist from '../../datasource/packagist'; -import { setExecConfig } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import * as docker from '../../util/exec/docker'; import type { StatusResult } from '../../util/git'; import * as hostRules from '../../util/host-rules'; @@ -46,11 +44,10 @@ const repoStatus = partial({ }); describe('.updateArtifacts()', () => { - beforeEach(async () => { + beforeEach(() => { jest.resetAllMocks(); jest.resetModules(); env.getChildProcessEnv.mockReturnValue(envMock.basic); - await setExecConfig(adminConfig as never); docker.resetPrefetchedImages(); hostRules.clear(); setAdminConfig(adminConfig); @@ -202,8 +199,7 @@ describe('.updateArtifacts()', () => { expect(execSnapshots).toMatchSnapshot(); }); it('supports docker mode', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readLocalFile.mockResolvedValueOnce('Current composer.lock' as any); const execSnapshots = mockExecAll(exec); @@ -234,6 +230,7 @@ describe('.updateArtifacts()', () => { expect(execSnapshots).toMatchSnapshot(); }); it('supports global mode', async () => { + setAdminConfig({ ...adminConfig, binarySource: 'global' }); fs.readLocalFile.mockResolvedValueOnce('Current composer.lock' as any); const execSnapshots = mockExecAll(exec); fs.readLocalFile.mockReturnValueOnce('New composer.lock' as any); @@ -246,10 +243,7 @@ describe('.updateArtifacts()', () => { packageFileName: 'composer.json', updatedDeps: [], newPackageFileContent: '{}', - config: { - ...config, - binarySource: BinarySource.Global, - }, + config, }) ).not.toBeNull(); expect(execSnapshots).toMatchSnapshot(); diff --git a/lib/manager/gomod/__snapshots__/artifacts.spec.ts.snap b/lib/manager/gomod/__snapshots__/artifacts.spec.ts.snap index 6e0ee06a4a9eb5..73e9ebdcdf8c23 100644 --- a/lib/manager/gomod/__snapshots__/artifacts.spec.ts.snap +++ b/lib/manager/gomod/__snapshots__/artifacts.spec.ts.snap @@ -631,7 +631,7 @@ Array [ }, }, Object { - "cmd": "mod upgrade --mod-name=github.com/google/go-github/v24 -t=28", + "cmd": "go mod upgrade --mod-name=github.com/google/go-github/v24 -t=28", "options": Object { "cwd": "/tmp/github/some/repo", "encoding": "utf-8", @@ -784,7 +784,7 @@ Array [ }, }, Object { - "cmd": "mod upgrade --mod-name=github.com/google/go-github/v24 -t=28", + "cmd": "go mod upgrade --mod-name=github.com/google/go-github/v24 -t=28", "options": Object { "cwd": "/tmp/github/some/repo", "encoding": "utf-8", @@ -937,7 +937,7 @@ Array [ }, }, Object { - "cmd": "mod upgrade --mod-name=github.com/google/go-github/v24 -t=28", + "cmd": "go mod upgrade --mod-name=github.com/google/go-github/v24 -t=28", "options": Object { "cwd": "/tmp/github/some/repo", "encoding": "utf-8", diff --git a/lib/manager/gomod/artifacts.spec.ts b/lib/manager/gomod/artifacts.spec.ts index 0d5f5db9480328..49033cba6c9d1a 100644 --- a/lib/manager/gomod/artifacts.spec.ts +++ b/lib/manager/gomod/artifacts.spec.ts @@ -5,8 +5,6 @@ import { envMock, mockExecAll } from '../../../test/exec-util'; import { git, mocked } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; -import { setExecConfig } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import * as docker from '../../util/exec/docker'; import * as _env from '../../util/exec/env'; import type { StatusResult } from '../../util/git'; @@ -57,13 +55,12 @@ const goEnv = { }; describe('.updateArtifacts()', () => { - beforeEach(async () => { + beforeEach(() => { jest.resetAllMocks(); jest.resetModules(); delete process.env.GOPATH; env.getChildProcessEnv.mockReturnValue({ ...envMock.basic, ...goEnv }); - await setExecConfig(adminConfig as never); setAdminConfig(adminConfig); docker.resetPrefetchedImages(); }); @@ -154,8 +151,7 @@ describe('.updateArtifacts()', () => { expect(execSnapshots).toMatchSnapshot(); }); it('supports docker mode without credentials', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readFile.mockResolvedValueOnce('Current go.sum' as any); fs.readFile.mockResolvedValueOnce(null as any); // vendor modules filename const execSnapshots = mockExecAll(exec); @@ -168,15 +164,13 @@ describe('.updateArtifacts()', () => { packageFileName: 'go.mod', updatedDeps: [], newPackageFileContent: gomod1, - config: { - ...config, - binarySource: BinarySource.Docker, - }, + config, }) ).not.toBeNull(); expect(execSnapshots).toMatchSnapshot(); }); it('supports global mode', async () => { + setAdminConfig({ ...adminConfig, binarySource: 'global' }); fs.readFile.mockResolvedValueOnce('Current go.sum' as any); fs.readFile.mockResolvedValueOnce(null as any); // vendor modules filename const execSnapshots = mockExecAll(exec); @@ -189,17 +183,13 @@ describe('.updateArtifacts()', () => { packageFileName: 'go.mod', updatedDeps: [], newPackageFileContent: gomod1, - config: { - ...config, - binarySource: BinarySource.Global, - }, + config, }) ).not.toBeNull(); expect(execSnapshots).toMatchSnapshot(); }); it('supports docker mode with credentials', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); hostRules.find.mockReturnValueOnce({ token: 'some-token', }); @@ -215,17 +205,13 @@ describe('.updateArtifacts()', () => { packageFileName: 'go.mod', updatedDeps: [], newPackageFileContent: gomod1, - config: { - ...config, - binarySource: BinarySource.Docker, - }, + config, }) ).not.toBeNull(); expect(execSnapshots).toMatchSnapshot(); }); it('supports docker mode with goModTidy', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); hostRules.find.mockReturnValueOnce({}); fs.readFile.mockResolvedValueOnce('Current go.sum' as any); fs.readFile.mockResolvedValueOnce(null as any); // vendor modules filename @@ -244,7 +230,6 @@ describe('.updateArtifacts()', () => { newPackageFileContent: gomod1, config: { ...config, - binarySource: BinarySource.Docker, postUpdateOptions: ['gomodTidy'], }, }) diff --git a/lib/manager/gomod/artifacts.ts b/lib/manager/gomod/artifacts.ts index 435d6a7b8ed1eb..da0b89a3e5aed7 100644 --- a/lib/manager/gomod/artifacts.ts +++ b/lib/manager/gomod/artifacts.ts @@ -1,11 +1,11 @@ import is from '@sindresorhus/is'; import { quote } from 'shlex'; import { dirname, join } from 'upath'; +import { getAdminConfig } from '../../config/admin'; import { TEMPORARY_ERROR } from '../../constants/error-messages'; import { PLATFORM_TYPE_GITHUB } from '../../constants/platforms'; import { logger } from '../../logger'; import { ExecOptions, exec } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import { ensureCacheDir, readLocalFile, writeLocalFile } from '../../util/fs'; import { getRepoStatus } from '../../util/git'; import { find } from '../../util/host-rules'; @@ -37,7 +37,7 @@ function getUpdateImportPathCmds( ): string[] { const updateImportCommands = updatedDeps .filter((x) => !x.startsWith('gopkg.in')) - .map((depName) => `mod upgrade --mod-name=${depName} -t=${newMajor}`); + .map((depName) => `go mod upgrade --mod-name=${depName} -t=${newMajor}`); if (updateImportCommands.length > 0) { let installMarwanModArgs = @@ -126,7 +126,7 @@ export async function updateArtifacts({ GONOPROXY: process.env.GONOPROXY, GONOSUMDB: process.env.GONOSUMDB, GOFLAGS: useModcacherw(config.constraints?.go) ? '-modcacherw' : null, - CGO_ENABLED: config.binarySource === BinarySource.Docker ? '0' : null, + CGO_ENABLED: getAdminConfig().binarySource === 'docker' ? '0' : null, }, docker: { image: 'go', diff --git a/lib/manager/gradle-wrapper/artifacts-real.spec.ts b/lib/manager/gradle-wrapper/artifacts-real.spec.ts index 3df946849148e1..0a4d365a8c2909 100644 --- a/lib/manager/gradle-wrapper/artifacts-real.spec.ts +++ b/lib/manager/gradle-wrapper/artifacts-real.spec.ts @@ -5,7 +5,6 @@ import * as httpMock from '../../../test/http-mock'; import { getName, git, partial } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; -import { setExecConfig } from '../../util/exec'; import type { StatusResult } from '../../util/git'; import { ifSystemSupportsGradle } from '../gradle/__testutil__/gradle'; import type { UpdateArtifactsConfig } from '../types'; @@ -41,16 +40,13 @@ describe(getName(), () => { ifSystemSupportsGradle(6).describe('real tests', () => { jest.setTimeout(60 * 1000); - beforeEach(async () => { + beforeEach(() => { jest.resetAllMocks(); - await setExecConfig(adminConfig as never); setAdminConfig(adminConfig); - httpMock.setup(); }); afterEach(async () => { await Git(fixtures).checkout(['HEAD', '--', '.']); - httpMock.reset(); setAdminConfig(); }); @@ -172,7 +168,6 @@ describe(getName(), () => { localDir: resolve(fixtures, './wrongCmd'), }; - await setExecConfig(wrongCmdConfig); setAdminConfig(wrongCmdConfig); const res = await dcUpdate.updateArtifacts({ packageFileName: 'gradle/wrapper/gradle-wrapper.properties', diff --git a/lib/manager/gradle-wrapper/artifacts.spec.ts b/lib/manager/gradle-wrapper/artifacts.spec.ts index 16f92f1dd04732..83a82e654706d3 100644 --- a/lib/manager/gradle-wrapper/artifacts.spec.ts +++ b/lib/manager/gradle-wrapper/artifacts.spec.ts @@ -14,8 +14,6 @@ import { } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; -import { setExecConfig } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import { resetPrefetchedImages } from '../../util/exec/docker'; import type { StatusResult } from '../../util/git'; import type { UpdateArtifactsConfig } from '../types'; @@ -33,7 +31,7 @@ const adminConfig: RepoAdminConfig = { localDir: resolve(fixtures, './testFiles'), }; -const dockerAdminConfig = { ...adminConfig, binarySource: BinarySource.Docker }; +const dockerAdminConfig = { ...adminConfig, binarySource: 'docker' }; const config: UpdateArtifactsConfig = { newValue: '5.6.4', @@ -47,9 +45,8 @@ function readString(...paths: string[]): Promise { } describe(getName(), () => { - beforeEach(async () => { + beforeEach(() => { jest.resetAllMocks(); - httpMock.setup(); env.getChildProcessEnv.mockReturnValue({ ...envMock.basic, @@ -57,7 +54,6 @@ describe(getName(), () => { LC_ALL: 'en_US', }); - await setExecConfig(adminConfig as never); setAdminConfig(adminConfig); resetPrefetchedImages(); @@ -65,7 +61,6 @@ describe(getName(), () => { }); afterEach(() => { - httpMock.reset(); setAdminConfig(); }); diff --git a/lib/manager/gradle-wrapper/extract.ts b/lib/manager/gradle-wrapper/extract.ts index 6aedb9b0ae6c3d..8bc09791875026 100644 --- a/lib/manager/gradle-wrapper/extract.ts +++ b/lib/manager/gradle-wrapper/extract.ts @@ -1,4 +1,4 @@ -import * as datasourceGradleVersion from '../../datasource/gradle-version'; +import { GradleVersionDatasource } from '../../datasource/gradle-version'; import { logger } from '../../logger'; import { regEx } from '../../util/regex'; import * as gradleVersioning from '../../versioning/gradle'; @@ -19,7 +19,7 @@ export function extractPackageFile(fileContent: string): PackageFile | null { const dependency: PackageDependency = { depName: 'gradle', currentValue: distributionUrlMatch.groups.version, - datasource: datasourceGradleVersion.id, + datasource: GradleVersionDatasource.id, versioning: gradleVersioning.id, }; logger.debug(dependency, 'Gradle Wrapper'); diff --git a/lib/manager/gradle/index.spec.ts b/lib/manager/gradle/index.spec.ts index 7171b2ac817aa2..7dbaecfdf7bcdf 100644 --- a/lib/manager/gradle/index.spec.ts +++ b/lib/manager/gradle/index.spec.ts @@ -12,8 +12,6 @@ import { } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; -import { setExecConfig } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import * as docker from '../../util/exec/docker'; import * as _env from '../../util/exec/env'; import type { ExtractConfig } from '../types'; @@ -34,7 +32,7 @@ const adminConfig: RepoAdminConfig = { const dockerAdminConfig = { ...adminConfig, - binarySource: BinarySource.Docker, + binarySource: 'docker', }; const gradleOutput = { @@ -88,8 +86,7 @@ describe(getName(), () => { return mockExecAll(exec, output); } - beforeAll(async () => { - await setExecConfig(adminConfig as never); + beforeAll(() => { setAdminConfig(adminConfig); }); @@ -215,8 +212,7 @@ describe(getName(), () => { }); it('should use docker if required', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig(dockerAdminConfig); + setAdminConfig(dockerAdminConfig); const execSnapshots = setupMocks({ wrapperFilename: null }); const dependencies = await extractAllPackageFiles(config, [ 'build.gradle', @@ -226,8 +222,7 @@ describe(getName(), () => { }); it('should use docker even if gradlew is available', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig(dockerAdminConfig); + setAdminConfig(dockerAdminConfig); const execSnapshots = setupMocks(); const dependencies = await extractAllPackageFiles(config, [ 'build.gradle', @@ -237,8 +232,7 @@ describe(getName(), () => { }); it('should use docker even if gradlew.bat is available on Windows', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig(dockerAdminConfig); + setAdminConfig(dockerAdminConfig); jest.spyOn(os, 'platform').mockReturnValueOnce('win32'); const execSnapshots = setupMocks({ wrapperFilename: 'gradlew.bat' }); const dependencies = await extractAllPackageFiles(config, [ diff --git a/lib/manager/gradle/utils.ts b/lib/manager/gradle/utils.ts index c648d7a286c70c..7430988ff4aa7d 100644 --- a/lib/manager/gradle/utils.ts +++ b/lib/manager/gradle/utils.ts @@ -2,7 +2,7 @@ import { Stats } from 'fs'; import os from 'os'; import { chmod } from 'fs-extra'; import upath from 'upath'; -import { BinarySource } from '../../util/exec/common'; +import { getAdminConfig } from '../../config/admin'; import type { ExtractConfig } from '../types'; export const extraEnv = { @@ -13,7 +13,7 @@ export const extraEnv = { export function gradleWrapperFileName(config: ExtractConfig): string { if ( os.platform() === 'win32' && - config?.binarySource !== BinarySource.Docker + getAdminConfig()?.binarySource !== 'docker' ) { return 'gradlew.bat'; } diff --git a/lib/manager/helm-requirements/extract.ts b/lib/manager/helm-requirements/extract.ts index 01bd698f36c46a..bea85701c77f89 100644 --- a/lib/manager/helm-requirements/extract.ts +++ b/lib/manager/helm-requirements/extract.ts @@ -1,6 +1,6 @@ import is from '@sindresorhus/is'; import { load } from 'js-yaml'; -import * as datasourceHelm from '../../datasource/helm'; +import { HelmDatasource } from '../../datasource/helm'; import { logger } from '../../logger'; import { SkipReason } from '../../types'; import type { ExtractConfig, PackageDependency, PackageFile } from '../types'; @@ -71,7 +71,7 @@ export function extractPackageFile( }); const res = { deps, - datasource: datasourceHelm.id, + datasource: HelmDatasource.id, }; return res; } diff --git a/lib/manager/helmfile/extract.ts b/lib/manager/helmfile/extract.ts index 66d47da65cfa85..f15090b1813f54 100644 --- a/lib/manager/helmfile/extract.ts +++ b/lib/manager/helmfile/extract.ts @@ -1,6 +1,6 @@ import is from '@sindresorhus/is'; import { loadAll } from 'js-yaml'; -import * as datasourceHelm from '../../datasource/helm'; +import { HelmDatasource } from '../../datasource/helm'; import { logger } from '../../logger'; import { SkipReason } from '../../types'; import type { ExtractConfig, PackageDependency, PackageFile } from '../types'; @@ -88,5 +88,5 @@ export function extractPackageFile( return null; } - return { deps, datasource: datasourceHelm.id } as PackageFile; + return { deps, datasource: HelmDatasource.id } as PackageFile; } diff --git a/lib/manager/helmv3/artifacts.spec.ts b/lib/manager/helmv3/artifacts.spec.ts index 0fcb69da2be1ec..5b34ac041207f7 100644 --- a/lib/manager/helmv3/artifacts.spec.ts +++ b/lib/manager/helmv3/artifacts.spec.ts @@ -5,8 +5,6 @@ import { envMock, mockExecAll } from '../../../test/exec-util'; import { git, mocked } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; -import { setExecConfig } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import * as docker from '../../util/exec/docker'; import * as _env from '../../util/exec/env'; import type { UpdateArtifactsConfig } from '../types'; @@ -29,12 +27,11 @@ const adminConfig: RepoAdminConfig = { const config: UpdateArtifactsConfig = {}; describe('.updateArtifacts()', () => { - beforeEach(async () => { + beforeEach(() => { jest.resetAllMocks(); jest.resetModules(); env.getChildProcessEnv.mockReturnValue(envMock.basic); - await setExecConfig(adminConfig as never); setAdminConfig(adminConfig); docker.resetPrefetchedImages(); }); @@ -109,8 +106,7 @@ describe('.updateArtifacts()', () => { }); it('returns updated Chart.lock with docker', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); git.getFile.mockResolvedValueOnce('Old Chart.lock'); const execSnapshots = mockExecAll(exec); fs.readFile.mockResolvedValueOnce('New Chart.lock' as any); diff --git a/lib/manager/helmv3/extract.ts b/lib/manager/helmv3/extract.ts index b19cb3d79a6d19..d589d3403c260b 100644 --- a/lib/manager/helmv3/extract.ts +++ b/lib/manager/helmv3/extract.ts @@ -1,6 +1,6 @@ import is from '@sindresorhus/is'; import { load } from 'js-yaml'; -import * as datasourceHelm from '../../datasource/helm'; +import { HelmDatasource } from '../../datasource/helm'; import { logger } from '../../logger'; import { SkipReason } from '../../types'; import { getSiblingFileName, localPathExists } from '../../util/fs'; @@ -90,7 +90,7 @@ export async function extractPackageFile( }); const res: PackageFile = { deps, - datasource: datasourceHelm.id, + datasource: HelmDatasource.id, packageFileVersion, }; const lockFileName = getSiblingFileName(fileName, 'Chart.lock'); diff --git a/lib/manager/homebrew/update.spec.ts b/lib/manager/homebrew/update.spec.ts index 1ffe47fabcafb4..44b6618413cd7f 100644 --- a/lib/manager/homebrew/update.spec.ts +++ b/lib/manager/homebrew/update.spec.ts @@ -12,11 +12,6 @@ describe(getName(), () => { beforeEach(() => { jest.resetAllMocks(); jest.resetModules(); - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); }); it('updates "releases" github dependency', async () => { diff --git a/lib/manager/leiningen/__fixtures__/project.clj b/lib/manager/leiningen/__fixtures__/project.clj index 0c145cd6ef8948..1f6714ddb5319c 100644 --- a/lib/manager/leiningen/__fixtures__/project.clj +++ b/lib/manager/leiningen/__fixtures__/project.clj @@ -1,3 +1,4 @@ +(def clj-stacktrace-version "0.2.4") ;; This is an annotated reference of the options that may be set in a ;; project.clj file. It is fairly contrived in order to cover lots of ;; different options; it shouldn't be considered a representative @@ -184,7 +185,7 @@ :1.5 {:dependencies [[org.clojure/clojure "1.5.0"]]} ;; activated by default :dev {:resource-paths ["dummy-data"] - :dependencies [[clj-stacktrace "0.2.4"]]} + :dependencies [[clj-stacktrace ~clj-stacktrace-version]]} ;; activated automatically during uberjar :uberjar {:aot :all} ;; activated automatically in repl task @@ -359,9 +360,9 @@ ;;; Repl ;; Options to change the way the REPL behaves. - :repl-options { ;; Specify the string to print when prompting for input. + :repl-options {;; Specify the string to print when prompting for input. ;; defaults to something like (fn [ns] (str *ns* "=> ")) - :prompt (fn [ns] (str "your command for <" ns ">? " )) + :prompt (fn [ns] (str "your command for <" ns ">? ")) ;; What to print when the repl session starts. :welcome (println "Welcome to the magical world of the repl!") ;; Specify the ns to start the REPL in (overrides :main in diff --git a/lib/manager/leiningen/extract.spec.ts b/lib/manager/leiningen/extract.spec.ts index 219fac2d98fc5f..014c8d27e7546b 100644 --- a/lib/manager/leiningen/extract.spec.ts +++ b/lib/manager/leiningen/extract.spec.ts @@ -1,6 +1,11 @@ import { getName, loadFixture } from '../../../test/util'; import { ClojureDatasource } from '../../datasource/clojure'; -import { extractFromVectors, extractPackageFile, trimAtKey } from './extract'; +import { + extractFromVectors, + extractPackageFile, + extractVariables, + trimAtKey, +} from './extract'; const leinProjectClj = loadFixture(`project.clj`); @@ -23,6 +28,15 @@ describe(getName(), () => { currentValue: '1.2.3', }, ]); + expect( + extractFromVectors('[[foo/bar ~baz]]', {}, { baz: '1.2.3' }) + ).toEqual([ + { + datasource: ClojureDatasource.id, + depName: 'foo:bar', + currentValue: '1.2.3', + }, + ]); expect( extractFromVectors('[\t[foo/bar "1.2.3"]\n["foo/baz" "4.5.6"] ]') ).toEqual([ @@ -41,4 +55,12 @@ describe(getName(), () => { it('extractPackageFile', () => { expect(extractPackageFile(leinProjectClj)).toMatchSnapshot(); }); + it('extractVariables', () => { + expect(extractVariables('(def foo "1")')).toEqual({ foo: '1' }); + expect(extractVariables('(def foo"2")')).toEqual({ foo: '2' }); + expect(extractVariables('(def foo "3")\n(def bar "4")')).toEqual({ + foo: '3', + bar: '4', + }); + }); }); diff --git a/lib/manager/leiningen/extract.ts b/lib/manager/leiningen/extract.ts index d34b8e7deb70ab..e219db21b20f06 100644 --- a/lib/manager/leiningen/extract.ts +++ b/lib/manager/leiningen/extract.ts @@ -1,6 +1,6 @@ import { ClojureDatasource } from '../../datasource/clojure'; import type { PackageDependency, PackageFile } from '../types'; -import type { ExtractContext } from './types'; +import type { ExtractContext, ExtractedVariables } from './types'; export function trimAtKey(str: string, kwName: string): string | null { const regex = new RegExp(`:${kwName}(?=\\s)`); @@ -22,8 +22,8 @@ export function expandDepName(name: string): string { export function extractFromVectors( str: string, - offset = 0, - ctx: ExtractContext = {} + ctx: ExtractContext = {}, + vars: ExtractedVariables = {} ): PackageDependency[] { if (!str.startsWith('[')) { return []; @@ -34,7 +34,6 @@ export function extractFromVectors( let vecPos = 0; let artifactId = ''; let version = ''; - let fileReplacePosition: number = null; const isSpace = (ch: string): boolean => ch && /[\s,]/.test(ch); @@ -42,13 +41,26 @@ export function extractFromVectors( s.replace(/^"/, '').replace(/"$/, ''); const yieldDep = (): void => { - if (artifactId && version && fileReplacePosition) { - result.push({ - ...ctx, - datasource: ClojureDatasource.id, - depName: expandDepName(cleanStrLiteral(artifactId)), - currentValue: cleanStrLiteral(version), - }); + if (artifactId && version) { + const depName = expandDepName(cleanStrLiteral(artifactId)); + if (version.startsWith('~')) { + const currentValue = vars[version.replace(/^~\s*/, '')]; + if (currentValue) { + result.push({ + ...ctx, + datasource: ClojureDatasource.id, + depName, + currentValue, + }); + } + } else { + result.push({ + ...ctx, + datasource: ClojureDatasource.id, + depName, + currentValue: cleanStrLiteral(version), + }); + } } artifactId = ''; version = ''; @@ -77,9 +89,6 @@ export function extractFromVectors( } else if (vecPos === 0) { artifactId += char; } else if (vecPos === 1) { - if (isSpace(prevChar)) { - fileReplacePosition = offset + idx + 1; - } version += char; } } @@ -121,41 +130,50 @@ function extractLeinRepos(content: string): string[] { return result; } +const defRegex = + /^[\s,]*\([\s,]*def[\s,]+(?[-+*=<>.!?#$%&_|a-zA-Z][-+*=<>.!?#$%&_|a-zA-Z0-9']+)[\s,]*"(?[^"]*)"[\s,]*\)[\s,]*$/; + +export function extractVariables(content: string): ExtractedVariables { + const result: ExtractedVariables = {}; + const lines = content.split('\n'); + for (let idx = 0; idx < lines.length; idx += 1) { + const line = lines[idx]; + const match = defRegex.exec(line); + if (match) { + const { varName: key, stringValue: val } = match.groups; + result[key] = val; + } + } + return result; +} + export function extractPackageFile(content: string): PackageFile { - const collect = (key: string, ctx: ExtractContext): PackageDependency[] => { + const collect = ( + key: string, + registryUrls: string[], + vars: ExtractedVariables + ): PackageDependency[] => { + const ctx = { + depType: key, + registryUrls, + }; let result: PackageDependency[] = []; let restContent = trimAtKey(content, key); while (restContent) { - const offset = content.length - restContent.length; - result = [...result, ...extractFromVectors(restContent, offset, ctx)]; + result = [...result, ...extractFromVectors(restContent, ctx, vars)]; restContent = trimAtKey(restContent, key); } return result; }; const registryUrls = extractLeinRepos(content); + const vars = extractVariables(content); const deps: PackageDependency[] = [ - ...collect('dependencies', { - depType: 'dependencies', - registryUrls, - }), - ...collect('managed-dependencies', { - depType: 'managed-dependencies', - registryUrls, - }), - ...collect('dev-dependencies', { - depType: 'managed-dependencies', - registryUrls, - }), - ...collect('plugins', { - depType: 'plugins', - registryUrls, - }), - ...collect('pom-plugins', { - depType: 'pom-plugins', - registryUrls, - }), + ...collect('dependencies', registryUrls, vars), + ...collect('managed-dependencies', registryUrls, vars), + ...collect('plugins', registryUrls, vars), + ...collect('pom-plugins', registryUrls, vars), ]; return { deps }; diff --git a/lib/manager/leiningen/types.ts b/lib/manager/leiningen/types.ts index 940f428932afd6..9c90ce6390a7d2 100644 --- a/lib/manager/leiningen/types.ts +++ b/lib/manager/leiningen/types.ts @@ -1,3 +1,5 @@ +export type ExtractedVariables = Record; + export interface ExtractContext { depType?: string; registryUrls?: string[]; diff --git a/lib/manager/mix/artifacts.spec.ts b/lib/manager/mix/artifacts.spec.ts index 16824a564bb1c7..376a7f187585dc 100644 --- a/lib/manager/mix/artifacts.spec.ts +++ b/lib/manager/mix/artifacts.spec.ts @@ -3,8 +3,6 @@ import { envMock, exec, mockExecAll } from '../../../test/exec-util'; import { env, fs, getName } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; -import { setExecConfig } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import * as docker from '../../util/exec/docker'; import type { UpdateArtifactsConfig } from '../types'; import { updateArtifacts } from '.'; @@ -21,12 +19,11 @@ const adminConfig: RepoAdminConfig = { const config: UpdateArtifactsConfig = {}; describe(getName(), () => { - beforeEach(async () => { + beforeEach(() => { jest.resetAllMocks(); jest.resetModules(); env.getChildProcessEnv.mockReturnValue(envMock.basic); - await setExecConfig(adminConfig as never); setAdminConfig(adminConfig); }); @@ -84,7 +81,7 @@ describe(getName(), () => { it('returns updated mix.lock', async () => { jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readLocalFile.mockResolvedValueOnce('Old mix.lock'); const execSnapshots = mockExecAll(exec); fs.readLocalFile.mockResolvedValueOnce('New mix.lock'); diff --git a/lib/manager/npm/__fixtures__/pnpm-monorepo/nested-packages/group/a/package.json b/lib/manager/npm/__fixtures__/pnpm-monorepo/nested-packages/group/a/package.json new file mode 100644 index 00000000000000..279df4bbc36856 --- /dev/null +++ b/lib/manager/npm/__fixtures__/pnpm-monorepo/nested-packages/group/a/package.json @@ -0,0 +1,12 @@ +{ + "name": "@demo/nested-group-a", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "dotenv": "^9.0.2" + } +} diff --git a/lib/manager/npm/__fixtures__/pnpm-monorepo/nested-packages/group/b/package.json b/lib/manager/npm/__fixtures__/pnpm-monorepo/nested-packages/group/b/package.json new file mode 100644 index 00000000000000..1081c3c0130684 --- /dev/null +++ b/lib/manager/npm/__fixtures__/pnpm-monorepo/nested-packages/group/b/package.json @@ -0,0 +1,12 @@ +{ + "name": "@demo/nested-group-b", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "chalk": "^2.4.1" + } +} diff --git a/lib/manager/npm/__fixtures__/pnpm-monorepo/non-nested-packages/a/package.json b/lib/manager/npm/__fixtures__/pnpm-monorepo/non-nested-packages/a/package.json new file mode 100644 index 00000000000000..101face8cbf63c --- /dev/null +++ b/lib/manager/npm/__fixtures__/pnpm-monorepo/non-nested-packages/a/package.json @@ -0,0 +1,12 @@ +{ + "name": "@demo/non-nested-a", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "dotenv": "^9.0.2" + } +} diff --git a/lib/manager/npm/__fixtures__/pnpm-monorepo/packages/b/package.json b/lib/manager/npm/__fixtures__/pnpm-monorepo/non-nested-packages/b/package.json similarity index 85% rename from lib/manager/npm/__fixtures__/pnpm-monorepo/packages/b/package.json rename to lib/manager/npm/__fixtures__/pnpm-monorepo/non-nested-packages/b/package.json index 6a9a836db99568..cf42bf384bd3db 100644 --- a/lib/manager/npm/__fixtures__/pnpm-monorepo/packages/b/package.json +++ b/lib/manager/npm/__fixtures__/pnpm-monorepo/non-nested-packages/b/package.json @@ -1,5 +1,5 @@ { - "name": "@demo/a", + "name": "@demo/non-nested-b", "version": "1.0.0", "description": "", "main": "index.js", diff --git a/lib/manager/npm/__fixtures__/pnpm-monorepo/pnpm-lock.yaml b/lib/manager/npm/__fixtures__/pnpm-monorepo/pnpm-lock.yaml index 7703c553f1473e..b0b4677c38533a 100644 --- a/lib/manager/npm/__fixtures__/pnpm-monorepo/pnpm-lock.yaml +++ b/lib/manager/npm/__fixtures__/pnpm-monorepo/pnpm-lock.yaml @@ -8,18 +8,42 @@ importers: dependencies: chalk: 2.4.2 - packages/a: + nested-packages/group/a: specifiers: dotenv: ^9.0.2 dependencies: dotenv: 9.0.2 - packages/b: + nested-packages/group/b: specifiers: chalk: ^2.4.1 dependencies: chalk: 2.4.2 + non-nested-packages/a: + specifiers: + dotenv: ^9.0.2 + dependencies: + dotenv: 9.0.2 + + non-nested-packages/b: + specifiers: + chalk: ^2.4.1 + dependencies: + chalk: 2.4.2 + + solo-package: + specifiers: + dotenv: ^9.0.2 + dependencies: + dotenv: 9.0.2 + + solo-package-trailing-slash: + specifiers: + dotenv: ^9.0.2 + dependencies: + dotenv: 9.0.2 + packages: /ansi-styles/3.2.1: diff --git a/lib/manager/npm/__fixtures__/pnpm-monorepo/pnpm-workspace.yaml b/lib/manager/npm/__fixtures__/pnpm-monorepo/pnpm-workspace.yaml index 14d2f77b700d8d..d628e29b10fcbe 100644 --- a/lib/manager/npm/__fixtures__/pnpm-monorepo/pnpm-workspace.yaml +++ b/lib/manager/npm/__fixtures__/pnpm-monorepo/pnpm-workspace.yaml @@ -1,3 +1,6 @@ packages: # all packages in subdirs of packages/ - - 'packages/**' + - 'nested-packages/**' + - 'non-nested-packages/*' + - 'solo-package' + - 'solo-package-trailing-slash/' diff --git a/lib/manager/npm/__fixtures__/pnpm-monorepo/solo-package-trailing-slash/package.json b/lib/manager/npm/__fixtures__/pnpm-monorepo/solo-package-trailing-slash/package.json new file mode 100644 index 00000000000000..46cce36062a807 --- /dev/null +++ b/lib/manager/npm/__fixtures__/pnpm-monorepo/solo-package-trailing-slash/package.json @@ -0,0 +1,12 @@ +{ + "name": "@demo/solo-trailing-slash", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "dotenv": "^9.0.2" + } +} diff --git a/lib/manager/npm/__fixtures__/pnpm-monorepo/packages/a/package.json b/lib/manager/npm/__fixtures__/pnpm-monorepo/solo-package/package.json similarity index 88% rename from lib/manager/npm/__fixtures__/pnpm-monorepo/packages/a/package.json rename to lib/manager/npm/__fixtures__/pnpm-monorepo/solo-package/package.json index 979ce6d9817bf8..09326319de9a4d 100644 --- a/lib/manager/npm/__fixtures__/pnpm-monorepo/packages/a/package.json +++ b/lib/manager/npm/__fixtures__/pnpm-monorepo/solo-package/package.json @@ -1,5 +1,5 @@ { - "name": "@demo/a", + "name": "@demo/solo", "version": "1.0.0", "description": "", "main": "index.js", diff --git a/lib/manager/npm/extract/__snapshots__/pnpm.spec.ts.snap b/lib/manager/npm/extract/__snapshots__/pnpm.spec.ts.snap index c4e3102dcdcb6b..6c4a4b65286334 100644 --- a/lib/manager/npm/extract/__snapshots__/pnpm.spec.ts.snap +++ b/lib/manager/npm/extract/__snapshots__/pnpm.spec.ts.snap @@ -7,13 +7,13 @@ Array [ "pnpmShrinkwrap": "pnpm-lock.yaml", }, Object { - "packageFile": "packages/a/package.json", - "packageJsonName": "@org/a", + "packageFile": "nested-packages/group/a/package.json", + "packageJsonName": "@demo/nested-group-a", "pnpmShrinkwrap": "pnpm-lock.yaml", }, Object { "packageFile": "not-matching/b/package.json", - "packageJsonName": "@org/b", + "packageJsonName": "@not-matching/b", "pnpmShrinkwrap": undefined, }, ] @@ -35,13 +35,33 @@ Array [ "pnpmShrinkwrap": "pnpm-lock.yaml", }, Object { - "packageFile": "packages/a/package.json", - "packageJsonName": "@org/a", + "packageFile": "nested-packages/group/a/package.json", + "packageJsonName": "@demo/nested-group-a", "pnpmShrinkwrap": "pnpm-lock.yaml", }, Object { - "packageFile": "packages/b/package.json", - "packageJsonName": "@org/b", + "packageFile": "nested-packages/group/b/package.json", + "packageJsonName": "@demo/nested-group-b", + "pnpmShrinkwrap": "pnpm-lock.yaml", + }, + Object { + "packageFile": "non-nested-packages/a/package.json", + "packageJsonName": "@demo/non-nested-a", + "pnpmShrinkwrap": "pnpm-lock.yaml", + }, + Object { + "packageFile": "non-nested-packages/b/package.json", + "packageJsonName": "@demo/non-nested-b", + "pnpmShrinkwrap": "pnpm-lock.yaml", + }, + Object { + "packageFile": "solo-package/package.json", + "packageJsonName": "@demo/solo", + "pnpmShrinkwrap": "pnpm-lock.yaml", + }, + Object { + "packageFile": "solo-package-trailing-slash/package.json", + "packageJsonName": "@demo/solo-trailing-slash", "pnpmShrinkwrap": "pnpm-lock.yaml", }, ] diff --git a/lib/manager/npm/extract/pnpm.spec.ts b/lib/manager/npm/extract/pnpm.spec.ts index 72ce6298ac5f22..77ba95585e0672 100644 --- a/lib/manager/npm/extract/pnpm.spec.ts +++ b/lib/manager/npm/extract/pnpm.spec.ts @@ -89,13 +89,33 @@ describe(getName(), () => { pnpmShrinkwrap: 'pnpm-lock.yaml', }, { - packageFile: 'packages/a/package.json', - packageJsonName: '@org/a', + packageFile: 'nested-packages/group/a/package.json', + packageJsonName: '@demo/nested-group-a', pnpmShrinkwrap: undefined as undefined | string, }, { - packageFile: 'packages/b/package.json', - packageJsonName: '@org/b', + packageFile: 'nested-packages/group/b/package.json', + packageJsonName: '@demo/nested-group-b', + pnpmShrinkwrap: undefined as undefined | string, + }, + { + packageFile: 'non-nested-packages/a/package.json', + packageJsonName: '@demo/non-nested-a', + pnpmShrinkwrap: undefined as undefined | string, + }, + { + packageFile: 'non-nested-packages/b/package.json', + packageJsonName: '@demo/non-nested-b', + pnpmShrinkwrap: undefined as undefined | string, + }, + { + packageFile: 'solo-package/package.json', + packageJsonName: '@demo/solo', + pnpmShrinkwrap: undefined as undefined | string, + }, + { + packageFile: 'solo-package-trailing-slash/package.json', + packageJsonName: '@demo/solo-trailing-slash', pnpmShrinkwrap: undefined as undefined | string, }, ]; @@ -126,13 +146,13 @@ describe(getName(), () => { pnpmShrinkwrap: 'pnpm-lock.yaml', }, { - packageFile: 'packages/a/package.json', - packageJsonName: '@org/a', + packageFile: 'nested-packages/group/a/package.json', + packageJsonName: '@demo/nested-group-a', pnpmShrinkwrap: undefined as undefined | string, }, { packageFile: 'not-matching/b/package.json', - packageJsonName: '@org/b', + packageJsonName: '@not-matching/b', pnpmShrinkwrap: undefined as undefined | string, }, ]; diff --git a/lib/manager/npm/extract/pnpm.ts b/lib/manager/npm/extract/pnpm.ts index 2e6baa97c1435c..022ef6c8f4f9f6 100644 --- a/lib/manager/npm/extract/pnpm.ts +++ b/lib/manager/npm/extract/pnpm.ts @@ -102,7 +102,11 @@ export async function detectPnpmWorkspaces( } const packageFilters = packageFilterCache.get(workspaceYamlPath); const isPackageInWorkspace = - packageFilters !== null && matchesAnyPattern(packageFile, packageFilters); + packageFilters !== null && + matchesAnyPattern( + packageFile, + packageFilters.map((filter) => filter.replace(/\/?$/, '/package.json')) + ); if (isPackageInWorkspace) { p.pnpmShrinkwrap = lockFilePath; } else { diff --git a/lib/manager/npm/extract/utils.spec.ts b/lib/manager/npm/extract/utils.spec.ts new file mode 100644 index 00000000000000..a818ae5b435f6b --- /dev/null +++ b/lib/manager/npm/extract/utils.spec.ts @@ -0,0 +1,42 @@ +import { getName } from '../../../../test/util'; +import { matchesAnyPattern } from './utils'; + +describe(getName(), () => { + describe('.matchesAnyPattern()', () => { + it('matches package in nested directory', () => { + const packageFile = 'packages/group/a/package.json'; + const packageFilters = ['packages/**']; + + const isPackageInWorkspace = matchesAnyPattern( + packageFile, + packageFilters + ); + + expect(isPackageInWorkspace).toBeTrue(); + }); + + it('matches package in non-nested directory', () => { + const packageFile = 'non-nested-packages/a/package.json'; + const packageFilters = ['non-nested-packages/*/*']; + + const isPackageInWorkspace = matchesAnyPattern( + packageFile, + packageFilters + ); + + expect(isPackageInWorkspace).toBeTrue(); + }); + + it('matches package in explicitly defined directory', () => { + const packageFile = 'solo-package/package.json'; + const packageFilters = ['solo-package/*']; + + const isPackageInWorkspace = matchesAnyPattern( + packageFile, + packageFilters + ); + + expect(isPackageInWorkspace).toBeTrue(); + }); + }); +}); diff --git a/lib/manager/npm/post-update/index.ts b/lib/manager/npm/post-update/index.ts index 6549253827928a..c69fc644afd38d 100644 --- a/lib/manager/npm/post-update/index.ts +++ b/lib/manager/npm/post-update/index.ts @@ -662,7 +662,7 @@ export async function getAdditionalFiles( } artifactErrors.push({ lockFile: pnpmShrinkwrap, - stderr: res.stderr, + stderr: res.stderr || res.stdout, }); } else { const existingContent = await getFile( diff --git a/lib/manager/npm/post-update/npm.spec.ts b/lib/manager/npm/post-update/npm.spec.ts index b896545b9bde2e..5add5ecd82691a 100644 --- a/lib/manager/npm/post-update/npm.spec.ts +++ b/lib/manager/npm/post-update/npm.spec.ts @@ -3,7 +3,6 @@ import upath from 'upath'; import { envMock, mockExecAll } from '../../../../test/exec-util'; import { mocked } from '../../../../test/util'; -import { BinarySource } from '../../../util/exec/common'; import * as _env from '../../../util/exec/env'; import * as _fs from '../../../util/fs/proxies'; import * as npmHelper from './npm'; @@ -120,7 +119,7 @@ describe('generateLockFile', () => { const execSnapshots = mockExecAll(exec); fs.readFile = jest.fn(() => 'package-lock-contents') as never; const skipInstalls = false; - const binarySource = BinarySource.Global; + const binarySource = 'global'; const res = await npmHelper.generateLockFile( 'some-dir', {}, @@ -135,7 +134,7 @@ describe('generateLockFile', () => { it('runs twice if remediating', async () => { const execSnapshots = mockExecAll(exec); fs.readFile = jest.fn(() => 'package-lock-contents') as never; - const binarySource = BinarySource.Global; + const binarySource = 'global'; const res = await npmHelper.generateLockFile( 'some-dir', {}, @@ -182,7 +181,7 @@ describe('generateLockFile', () => { 'some-dir', {}, 'package-lock.json', - { binarySource: BinarySource.Docker, constraints: { npm: '^6.0.0' } } + { binarySource: 'docker', constraints: { npm: '^6.0.0' } } ); expect(fs.readFile).toHaveBeenCalledTimes(1); expect(res.lockFile).toEqual('package-lock-contents'); diff --git a/lib/manager/npm/update/locked-dependency/__snapshots__/index.spec.ts.snap b/lib/manager/npm/update/locked-dependency/__snapshots__/index.spec.ts.snap new file mode 100644 index 00000000000000..3f56c989b58cb3 --- /dev/null +++ b/lib/manager/npm/update/locked-dependency/__snapshots__/index.spec.ts.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`manager/npm/update/locked-dependency/index updateLockedDependency() remediates mime 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "registry.npmjs.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.npmjs.org/mime", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "registry.npmjs.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.npmjs.org/send", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "registry.npmjs.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.npmjs.org/type-is", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "registry.npmjs.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.npmjs.org/serve-static", + }, +] +`; diff --git a/lib/manager/npm/update/locked-dependency/__snapshots__/parent-version.spec.ts.snap b/lib/manager/npm/update/locked-dependency/__snapshots__/parent-version.spec.ts.snap new file mode 100644 index 00000000000000..02a90909a0a3df --- /dev/null +++ b/lib/manager/npm/update/locked-dependency/__snapshots__/parent-version.spec.ts.snap @@ -0,0 +1,86 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`manager/npm/update/locked-dependency/parent-version getLockedDependencies() finds indirect dependency 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "registry.npmjs.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.npmjs.org/send", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "registry.npmjs.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.npmjs.org/express", + }, +] +`; + +exports[`manager/npm/update/locked-dependency/parent-version getLockedDependencies() finds removed dependencies 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "registry.npmjs.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.npmjs.org/buffer-crc32", + }, +] +`; + +exports[`manager/npm/update/locked-dependency/parent-version getLockedDependencies() finds when a greater version is needed 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "registry.npmjs.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.npmjs.org/qs", + }, +] +`; + +exports[`manager/npm/update/locked-dependency/parent-version getLockedDependencies() finds when a range matches greater versions 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "registry.npmjs.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.npmjs.org/type-is", + }, +] +`; + +exports[`manager/npm/update/locked-dependency/parent-version getLockedDependencies() returns null if no matching 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "registry.npmjs.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.npmjs.org/debug", + }, +] +`; diff --git a/lib/manager/npm/update/locked-dependency/index.spec.ts b/lib/manager/npm/update/locked-dependency/index.spec.ts index 8b63d485f0b30c..36a688f6ab1a9a 100644 --- a/lib/manager/npm/update/locked-dependency/index.spec.ts +++ b/lib/manager/npm/update/locked-dependency/index.spec.ts @@ -17,7 +17,6 @@ describe(getName(), () => { describe('updateLockedDependency()', () => { let config: UpdateLockedConfig; beforeEach(() => { - httpMock.setup(); config = { packageFile: 'package.json', packageFileContent, @@ -28,9 +27,7 @@ describe(getName(), () => { newVersion: '1.0.1', }; }); - afterEach(() => { - httpMock.reset(); - }); + it('validates filename', async () => { expect( await updateLockedDependency({ ...config, lockFile: 'yarn.lock' }) @@ -114,14 +111,6 @@ describe(getName(), () => { config.depName = 'mime'; config.currentVersion = '1.2.11'; config.newVersion = '1.4.1'; - httpMock - .scope('https://registry.npmjs.org') - .get('/accepts') - .reply(200, acceptsJson); - httpMock - .scope('https://registry.npmjs.org') - .get('/express') - .reply(200, expressJson); httpMock .scope('https://registry.npmjs.org') .get('/mime') @@ -142,6 +131,7 @@ describe(getName(), () => { const packageLock = JSON.parse(res['package-lock.json']); expect(packageLock.dependencies.mime.version).toEqual('1.4.1'); expect(packageLock.dependencies.express.version).toEqual('4.16.0'); + expect(httpMock.getTrace()).toMatchSnapshot(); }); }); }); diff --git a/lib/manager/npm/update/locked-dependency/parent-version.spec.ts b/lib/manager/npm/update/locked-dependency/parent-version.spec.ts index 0415965d1b324e..5e9801319376cd 100644 --- a/lib/manager/npm/update/locked-dependency/parent-version.spec.ts +++ b/lib/manager/npm/update/locked-dependency/parent-version.spec.ts @@ -6,13 +6,6 @@ const expressJson = loadJsonFixture('express.json'); describe(getName(), () => { describe('getLockedDependencies()', () => { - beforeEach(() => { - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); - }); it('finds indirect dependency', async () => { httpMock .scope('https://registry.npmjs.org') @@ -32,10 +25,13 @@ describe(getName(), () => { .scope('https://registry.npmjs.org') .get('/express') .reply(200, expressJson); + expect( await findFirstParentVersion('express', '4.0.0', 'send', '0.11.1') ).toEqual('4.11.1'); + expect(httpMock.getTrace()).toMatchSnapshot(); }); + it('finds removed dependencies', async () => { httpMock .scope('https://registry.npmjs.org') @@ -48,10 +44,7 @@ describe(getName(), () => { }, 'dist-tags': { latest: '10.0.0' }, }); - httpMock - .scope('https://registry.npmjs.org') - .get('/express') - .reply(200, expressJson); + expect( await findFirstParentVersion( 'express', @@ -60,7 +53,9 @@ describe(getName(), () => { '10.0.0' ) ).toEqual('4.9.1'); + expect(httpMock.getTrace()).toMatchSnapshot(); }); + it('finds when a greater version is needed', async () => { httpMock .scope('https://registry.npmjs.org') @@ -75,14 +70,13 @@ describe(getName(), () => { }, 'dist-tags': { latest: '6.2.0' }, }); - httpMock - .scope('https://registry.npmjs.org') - .get('/express') - .reply(200, expressJson); + expect( await findFirstParentVersion('express', '4.0.0', 'qs', '6.0.4') ).toEqual('4.14.0'); + expect(httpMock.getTrace()).toMatchSnapshot(); }); + it('finds when a range matches greater versions', async () => { httpMock .scope('https://registry.npmjs.org') @@ -96,14 +90,13 @@ describe(getName(), () => { }, 'dist-tags': { latest: '1.6.15' }, }); - httpMock - .scope('https://registry.npmjs.org') - .get('/express') - .reply(200, expressJson); + expect( await findFirstParentVersion('express', '4.16.1', 'type-is', '1.2.1') ).toEqual('4.16.1'); + expect(httpMock.getTrace()).toMatchSnapshot(); }); + it('returns null if no matching', async () => { httpMock .scope('https://registry.npmjs.org') @@ -116,13 +109,11 @@ describe(getName(), () => { }, 'dist-tags': { latest: '10.0.0' }, }); - httpMock - .scope('https://registry.npmjs.org') - .get('/express') - .reply(200, expressJson); + expect( await findFirstParentVersion('express', '4.16.1', 'debug', '9.0.0') ).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); }); }); diff --git a/lib/manager/nuget/artifacts.spec.ts b/lib/manager/nuget/artifacts.spec.ts index 877d8de308442e..11066aa8ebfd52 100644 --- a/lib/manager/nuget/artifacts.spec.ts +++ b/lib/manager/nuget/artifacts.spec.ts @@ -4,8 +4,6 @@ import { envMock, mockExecAll } from '../../../test/exec-util'; import { fs, mocked } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; -import { setExecConfig } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import * as docker from '../../util/exec/docker'; import * as _env from '../../util/exec/env'; import * as _hostRules from '../../util/host-rules'; @@ -42,7 +40,7 @@ const adminConfig: RepoAdminConfig = { const config: UpdateArtifactsConfig = {}; describe('updateArtifacts', () => { - beforeEach(async () => { + beforeEach(() => { jest.resetAllMocks(); jest.resetModules(); getDefaultRegistries.mockReturnValue([] as any); @@ -51,7 +49,6 @@ describe('updateArtifacts', () => { Promise.resolve(dirName) ); getRandomString.mockReturnValue('not-so-random' as any); - await setExecConfig(adminConfig as never); setAdminConfig(adminConfig); docker.resetPrefetchedImages(); }); @@ -153,8 +150,7 @@ describe('updateArtifacts', () => { }); it('supports docker mode', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); const execSnapshots = mockExecAll(exec); fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json'); fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json' as any); @@ -170,6 +166,7 @@ describe('updateArtifacts', () => { expect(execSnapshots).toMatchSnapshot(); }); it('supports global mode', async () => { + setAdminConfig({ ...adminConfig, binarySource: 'global' }); const execSnapshots = mockExecAll(exec); fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json'); fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json' as any); @@ -179,10 +176,7 @@ describe('updateArtifacts', () => { packageFileName: 'project.csproj', updatedDeps: ['dep'], newPackageFileContent: '{}', - config: { - ...config, - binarySource: BinarySource.Global, - }, + config, }) ).not.toBeNull(); expect(execSnapshots).toMatchSnapshot(); diff --git a/lib/manager/pip-compile/__snapshots__/artifacts.spec.ts.snap b/lib/manager/pip-compile/__snapshots__/artifacts.spec.ts.snap new file mode 100644 index 00000000000000..7fa37078f87784 --- /dev/null +++ b/lib/manager/pip-compile/__snapshots__/artifacts.spec.ts.snap @@ -0,0 +1,151 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`.updateArtifacts() catches errors 1`] = ` +Array [ + Object { + "artifactError": Object { + "lockFile": "requirements.txt", + "stderr": "not found", + }, + }, +] +`; + +exports[`.updateArtifacts() returns null if unchanged 1`] = ` +Array [ + Object { + "cmd": "pip-compile", + "options": Object { + "cwd": "/tmp/github/some/repo", + "encoding": "utf-8", + "env": Object { + "HOME": "/home/user", + "HTTPS_PROXY": "https://example.com", + "HTTP_PROXY": "http://example.com", + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US", + "NO_PROXY": "localhost", + "PATH": "/tmp/path", + }, + "maxBuffer": 10485760, + "timeout": 900000, + }, + }, +] +`; + +exports[`.updateArtifacts() returns updated requirements.txt 1`] = ` +Array [ + Object { + "cmd": "pip-compile", + "options": Object { + "cwd": "/tmp/github/some/repo", + "encoding": "utf-8", + "env": Object { + "HOME": "/home/user", + "HTTPS_PROXY": "https://example.com", + "HTTP_PROXY": "http://example.com", + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US", + "NO_PROXY": "localhost", + "PATH": "/tmp/path", + }, + "maxBuffer": 10485760, + "timeout": 900000, + }, + }, +] +`; + +exports[`.updateArtifacts() returns updated requirements.txt when doing lockfile maintenance 1`] = ` +Array [ + Object { + "cmd": "pip-compile", + "options": Object { + "cwd": "/tmp/github/some/repo", + "encoding": "utf-8", + "env": Object { + "HOME": "/home/user", + "HTTPS_PROXY": "https://example.com", + "HTTP_PROXY": "http://example.com", + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US", + "NO_PROXY": "localhost", + "PATH": "/tmp/path", + }, + "maxBuffer": 10485760, + "timeout": 900000, + }, + }, +] +`; + +exports[`.updateArtifacts() supports docker mode 1`] = ` +Array [ + Object { + "cmd": "docker pull renovate/python", + "options": Object { + "encoding": "utf-8", + }, + }, + Object { + "cmd": "docker ps --filter name=renovate_python -aq", + "options": Object { + "encoding": "utf-8", + }, + }, + Object { + "cmd": "docker run --rm --name=renovate_python --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/renovate/cache\\":\\"/tmp/renovate/cache\\" -w \\"/tmp/github/some/repo\\" renovate/python bash -l -c \\"pip install --user pip-tools && pip-compile\\"", + "options": Object { + "cwd": "/tmp/github/some/repo", + "encoding": "utf-8", + "env": Object { + "HOME": "/home/user", + "HTTPS_PROXY": "https://example.com", + "HTTP_PROXY": "http://example.com", + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US", + "NO_PROXY": "localhost", + "PATH": "/tmp/path", + }, + "maxBuffer": 10485760, + "timeout": 900000, + }, + }, +] +`; + +exports[`.updateArtifacts() uses pipenv version from config 1`] = ` +Array [ + Object { + "cmd": "docker pull renovate/python", + "options": Object { + "encoding": "utf-8", + }, + }, + Object { + "cmd": "docker ps --filter name=renovate_python -aq", + "options": Object { + "encoding": "utf-8", + }, + }, + Object { + "cmd": "docker run --rm --name=renovate_python --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/renovate/cache\\":\\"/tmp/renovate/cache\\" -w \\"/tmp/github/some/repo\\" renovate/python bash -l -c \\"pip install --user pip-tools1.2.3 && pip-compile\\"", + "options": Object { + "cwd": "/tmp/github/some/repo", + "encoding": "utf-8", + "env": Object { + "HOME": "/home/user", + "HTTPS_PROXY": "https://example.com", + "HTTP_PROXY": "http://example.com", + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US", + "NO_PROXY": "localhost", + "PATH": "/tmp/path", + }, + "maxBuffer": 10485760, + "timeout": 900000, + }, + }, +] +`; diff --git a/lib/manager/pip-compile/artifacts.spec.ts b/lib/manager/pip-compile/artifacts.spec.ts new file mode 100644 index 00000000000000..cf8f52af8b7508 --- /dev/null +++ b/lib/manager/pip-compile/artifacts.spec.ts @@ -0,0 +1,159 @@ +import { exec as _exec } from 'child_process'; +import _fs from 'fs-extra'; +import { join } from 'upath'; +import { envMock, mockExecAll } from '../../../test/exec-util'; +import { git, mocked } from '../../../test/util'; +import { setAdminConfig } from '../../config/admin'; +import type { RepoAdminConfig } from '../../config/types'; +import * as docker from '../../util/exec/docker'; +import * as _env from '../../util/exec/env'; +import type { StatusResult } from '../../util/git'; +import type { UpdateArtifactsConfig } from '../types'; +import * as pipCompile from './artifacts'; + +jest.mock('fs-extra'); +jest.mock('child_process'); +jest.mock('../../util/exec/env'); +jest.mock('../../util/git'); +jest.mock('../../util/host-rules'); +jest.mock('../../util/http'); + +const fs: jest.Mocked = _fs as any; +const exec: jest.Mock = _exec as any; +const env = mocked(_env); + +const adminConfig: RepoAdminConfig = { + // `join` fixes Windows CI + localDir: join('/tmp/github/some/repo'), + cacheDir: join('/tmp/renovate/cache'), +}; +const dockerAdminConfig = { ...adminConfig, binarySource: 'docker' }; + +const config: UpdateArtifactsConfig = {}; +const lockMaintenanceConfig = { ...config, isLockFileMaintenance: true }; + +describe('.updateArtifacts()', () => { + beforeEach(() => { + jest.resetAllMocks(); + env.getChildProcessEnv.mockReturnValue({ + ...envMock.basic, + LANG: 'en_US.UTF-8', + LC_ALL: 'en_US', + }); + setAdminConfig(adminConfig); + docker.resetPrefetchedImages(); + }); + + it('returns if no requirements.txt found', async () => { + expect( + await pipCompile.updateArtifacts({ + packageFileName: 'requirements.in', + updatedDeps: [], + newPackageFileContent: '', + config, + }) + ).toBeNull(); + }); + + it('returns null if unchanged', async () => { + fs.readFile.mockResolvedValueOnce('content' as any); + const execSnapshots = mockExecAll(exec); + fs.readFile.mockReturnValueOnce('content' as any); + expect( + await pipCompile.updateArtifacts({ + packageFileName: 'requirements.in', + updatedDeps: [], + newPackageFileContent: 'some new content', + config, + }) + ).toBeNull(); + expect(execSnapshots).toMatchSnapshot(); + }); + + it('returns updated requirements.txt', async () => { + fs.readFile.mockResolvedValueOnce('current requirements.txt' as any); + const execSnapshots = mockExecAll(exec); + git.getRepoStatus.mockResolvedValue({ + modified: ['requirements.txt'], + } as StatusResult); + fs.readFile.mockReturnValueOnce('New requirements.txt' as any); + expect( + await pipCompile.updateArtifacts({ + packageFileName: 'requirements.in', + updatedDeps: [], + newPackageFileContent: 'some new content', + config: { ...config, constraints: { python: '3.7' } }, + }) + ).not.toBeNull(); + expect(execSnapshots).toMatchSnapshot(); + }); + + it('supports docker mode', async () => { + setAdminConfig(dockerAdminConfig); + const execSnapshots = mockExecAll(exec); + git.getRepoStatus.mockResolvedValue({ + modified: ['requirements.txt'], + } as StatusResult); + fs.readFile.mockReturnValueOnce('new lock' as any); + expect( + await pipCompile.updateArtifacts({ + packageFileName: 'requirements.in', + updatedDeps: [], + newPackageFileContent: 'some new content', + config, + }) + ).not.toBeNull(); + expect(execSnapshots).toMatchSnapshot(); + }); + + it('catches errors', async () => { + fs.readFile.mockResolvedValueOnce('Current requirements.txt' as any); + fs.outputFile.mockImplementationOnce(() => { + throw new Error('not found'); + }); + expect( + await pipCompile.updateArtifacts({ + packageFileName: 'requirements.in', + updatedDeps: [], + newPackageFileContent: '{}', + config, + }) + ).toMatchSnapshot(); + }); + + it('returns updated requirements.txt when doing lockfile maintenance', async () => { + fs.readFile.mockResolvedValueOnce('Current requirements.txt' as any); + const execSnapshots = mockExecAll(exec); + git.getRepoStatus.mockResolvedValue({ + modified: ['requirements.txt'], + } as StatusResult); + fs.readFile.mockReturnValueOnce('New requirements.txt' as any); + expect( + await pipCompile.updateArtifacts({ + packageFileName: 'requirements.in', + updatedDeps: [], + newPackageFileContent: '{}', + config: lockMaintenanceConfig, + }) + ).not.toBeNull(); + expect(execSnapshots).toMatchSnapshot(); + }); + + it('uses pipenv version from config', async () => { + setAdminConfig(dockerAdminConfig); + const execSnapshots = mockExecAll(exec); + git.getRepoStatus.mockResolvedValue({ + modified: ['requirements.txt'], + } as StatusResult); + fs.readFile.mockReturnValueOnce('new lock' as any); + expect( + await pipCompile.updateArtifacts({ + packageFileName: 'requirements.in', + updatedDeps: [], + newPackageFileContent: 'some new content', + config: { ...config, constraints: { pipTools: '1.2.3' } }, + }) + ).not.toBeNull(); + expect(execSnapshots).toMatchSnapshot(); + }); +}); diff --git a/lib/manager/pip-compile/artifacts.ts b/lib/manager/pip-compile/artifacts.ts new file mode 100644 index 00000000000000..58056c07d93dc8 --- /dev/null +++ b/lib/manager/pip-compile/artifacts.ts @@ -0,0 +1,103 @@ +import is from '@sindresorhus/is'; +import { quote as pipCompile } from 'shlex'; +import { TEMPORARY_ERROR } from '../../constants/error-messages'; +import { logger } from '../../logger'; +import { ExecOptions, exec } from '../../util/exec'; +import { deleteLocalFile, readLocalFile, writeLocalFile } from '../../util/fs'; +import { getRepoStatus } from '../../util/git'; +import type { + UpdateArtifact, + UpdateArtifactsConfig, + UpdateArtifactsResult, +} from '../types'; + +function getPythonConstraint( + config: UpdateArtifactsConfig +): string | undefined | null { + const { constraints = {} } = config; + const { python } = constraints; + + if (python) { + logger.debug('Using python constraint from config'); + return python; + } + + return undefined; +} + +function getPipToolsConstraint(config: UpdateArtifactsConfig): string { + const { constraints = {} } = config; + const { pipTools } = constraints; + + if (is.string(pipTools)) { + logger.debug('Using pipTools constraint from config'); + return pipTools; + } + + return ''; +} + +export async function updateArtifacts({ + packageFileName: inputFileName, + newPackageFileContent: newInputContent, + config, +}: UpdateArtifact): Promise { + const outputFileName = inputFileName.replace(/(\.in)?$/, '.txt'); + logger.debug( + `pipCompile.updateArtifacts(${inputFileName}->${outputFileName})` + ); + const existingOutput = await readLocalFile(outputFileName, 'utf8'); + if (!existingOutput) { + logger.debug('No pip-compile output file found'); + return null; + } + try { + await writeLocalFile(inputFileName, newInputContent); + if (config.isLockFileMaintenance) { + await deleteLocalFile(outputFileName); + } + const cmd = 'pip-compile'; + const tagConstraint = getPythonConstraint(config); + const pipToolsConstraint = getPipToolsConstraint(config); + const execOptions: ExecOptions = { + cwdFile: inputFileName, + docker: { + image: 'python', + tagConstraint, + tagScheme: 'pep440', + preCommands: [ + `pip install --user ${pipCompile(`pip-tools${pipToolsConstraint}`)}`, + ], + }, + }; + logger.debug({ cmd }, 'pip-compile command'); + await exec(cmd, execOptions); + const status = await getRepoStatus(); + if (!status?.modified.includes(outputFileName)) { + return null; + } + logger.debug('Returning updated pip-compile result'); + return [ + { + file: { + name: outputFileName, + contents: await readLocalFile(outputFileName, 'utf8'), + }, + }, + ]; + } catch (err) { + // istanbul ignore if + if (err.message === TEMPORARY_ERROR) { + throw err; + } + logger.debug({ err }, 'Failed to pip-compile'); + return [ + { + artifactError: { + lockFile: outputFileName, + stderr: err.message, + }, + }, + ]; + } +} diff --git a/lib/manager/pip-compile/index.ts b/lib/manager/pip-compile/index.ts new file mode 100644 index 00000000000000..c5d61f96a7002d --- /dev/null +++ b/lib/manager/pip-compile/index.ts @@ -0,0 +1,16 @@ +import { LANGUAGE_PYTHON } from '../../constants/languages'; + +export { extractPackageFile } from '../pip_requirements/extract'; +export { updateArtifacts } from './artifacts'; + +export const language = LANGUAGE_PYTHON; +export const supportsLockFileMaintenance = true; + +export const defaultConfig = { + fileMatch: [], + lockFileMaintenance: { + enabled: true, + branchTopic: 'pip-compile-refresh', + commitMessageAction: 'Refresh pip-compile outputs', + }, +}; diff --git a/lib/manager/pip-compile/readme.md b/lib/manager/pip-compile/readme.md new file mode 100644 index 00000000000000..42330b600641fb --- /dev/null +++ b/lib/manager/pip-compile/readme.md @@ -0,0 +1,42 @@ +Due to limited functionality, the `pip-compile` manager should be considered in an "alpha" stage, which means it's not ready for production use for the majority of end users. +We welcome feedback and bug reports! + +The current implementation has some limitations. +Read the full document before you start using the `pip-compile` manager. + +### Non-configured fileMatch + +The `pip-compile` manager has an empty array for default `fileMatch`, meaning it won't match any files ever by default. +You can "activate" the manager by specifying a `fileMatch` pattern such as: + +```json +{ + "pip-compile": { + "fileMatch": ["(^|/)requirements\\.in$"] + } +} +``` + +### Assumption of `.in`/`.txt` + +If Renovate matches/extracts a file, it assumes that the corresponding output file is found by swapping the `.in` for `.txt`. +e.g. `requirements.in` => `requirements.txt` + +Therefore it will not work if files are in separate directories, including `input/requirements.in` and `output/requirements.txt`. + +If no `.in` suffix is found, then a `.txt` suffix is appended for the output file, e.g. `foo.file` would look for a corresponding `foo.file.txt`. + +We intend to make the mapping configurable in future iterations. + +### Configuration of Python version + +By default Renovate uses the latest version of Python. +To get Renovate to use another version of Python, add a contraints` rule to the Renovate config: + +```json +{ + "constraints": { + "python": "3.7" + } +} +``` diff --git a/lib/manager/pip_setup/extract.ts b/lib/manager/pip_setup/extract.ts index cda26f734acb0f..762ff892c0cca6 100644 --- a/lib/manager/pip_setup/extract.ts +++ b/lib/manager/pip_setup/extract.ts @@ -1,8 +1,8 @@ +import { getAdminConfig } from '../../config/admin'; import * as datasourcePypi from '../../datasource/pypi'; import { logger } from '../../logger'; import { SkipReason } from '../../types'; import { exec } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import { isSkipComment } from '../../util/ignore'; import { dependencyPattern } from '../pip_requirements/extract'; import type { ExtractConfig, PackageDependency, PackageFile } from '../types'; @@ -49,7 +49,7 @@ export async function extractSetupFile( let cmd = 'python'; const extractPy = await getExtractFile(); const args = [`"${extractPy}"`, `"${packageFile}"`]; - if (config.binarySource !== BinarySource.Docker) { + if (getAdminConfig().binarySource !== 'docker') { logger.debug('Running python via global command'); cmd = await getPythonAlias(); } diff --git a/lib/manager/pip_setup/index.spec.ts b/lib/manager/pip_setup/index.spec.ts index fffa8ae536a337..e013963cf40ed6 100644 --- a/lib/manager/pip_setup/index.spec.ts +++ b/lib/manager/pip_setup/index.spec.ts @@ -8,8 +8,6 @@ import { import { env, getName, loadFixture } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; -import { setExecConfig } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import * as fs from '../../util/fs'; import type { ExtractConfig } from '../types'; import * as extract from './extract'; @@ -43,12 +41,11 @@ const fixSnapshots = (snapshots: ExecSnapshots): ExecSnapshots => describe(getName(), () => { describe('extractPackageFile()', () => { - beforeEach(async () => { + beforeEach(() => { jest.resetAllMocks(); jest.resetModules(); extract.resetModule(); - await setExecConfig(adminConfig as never); setAdminConfig(adminConfig); env.getChildProcessEnv.mockReturnValue(envMock.basic); @@ -78,18 +75,14 @@ describe(getName(), () => { }); it('returns found deps (docker)', async () => { - const execSnapshots = mockExecSequence(exec, [ - { stdout: '', stderr: '' }, - ]); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); + const execSnapshots = mockExecAll(exec, { stdout: '', stderr: '' }); jest.spyOn(fs, 'readLocalFile').mockResolvedValueOnce(jsonContent); expect( - await extractPackageFile(content, packageFile, { - ...config, - binarySource: BinarySource.Docker, - }) + await extractPackageFile(content, packageFile, config) ).toMatchSnapshot(); - expect(execSnapshots).toHaveLength(1); // TODO: figure out volume arguments in Windows (#9617) + expect(execSnapshots).toHaveLength(3); // TODO: figure out volume arguments in Windows (#9617) }); it('returns no deps', async () => { diff --git a/lib/manager/pipenv/artifacts.spec.ts b/lib/manager/pipenv/artifacts.spec.ts index 64951dc94edecc..5cc7b8c0fbb5b2 100644 --- a/lib/manager/pipenv/artifacts.spec.ts +++ b/lib/manager/pipenv/artifacts.spec.ts @@ -5,8 +5,6 @@ import { envMock, mockExecAll } from '../../../test/exec-util'; import { git, mocked } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; -import { setExecConfig } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import * as docker from '../../util/exec/docker'; import * as _env from '../../util/exec/env'; import type { StatusResult } from '../../util/git'; @@ -29,14 +27,14 @@ const adminConfig: RepoAdminConfig = { localDir: join('/tmp/github/some/repo'), cacheDir: join('/tmp/renovate/cache'), }; -const dockerAdminConfig = { ...adminConfig, binarySource: BinarySource.Docker }; +const dockerAdminConfig = { ...adminConfig, binarySource: 'docker' }; const config: UpdateArtifactsConfig = {}; const lockMaintenanceConfig = { ...config, isLockFileMaintenance: true }; describe('.updateArtifacts()', () => { let pipFileLock; - beforeEach(async () => { + beforeEach(() => { jest.resetAllMocks(); env.getChildProcessEnv.mockReturnValue({ ...envMock.basic, @@ -44,7 +42,6 @@ describe('.updateArtifacts()', () => { LC_ALL: 'en_US', }); - await setExecConfig(adminConfig as never); setAdminConfig(adminConfig); docker.resetPrefetchedImages(); pipFileLock = { @@ -111,8 +108,7 @@ describe('.updateArtifacts()', () => { expect(execSnapshots).toMatchSnapshot(); }); it('supports docker mode', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig(dockerAdminConfig); + setAdminConfig(dockerAdminConfig); pipFileLock._meta.requires.python_version = '3.7'; fs.readFile.mockResolvedValueOnce(JSON.stringify(pipFileLock) as any); const execSnapshots = mockExecAll(exec); @@ -125,7 +121,7 @@ describe('.updateArtifacts()', () => { packageFileName: 'Pipfile', updatedDeps: [], newPackageFileContent: 'some new content', - config: { ...config, ...dockerAdminConfig }, + config, }) ).not.toBeNull(); expect(execSnapshots).toMatchSnapshot(); @@ -162,8 +158,7 @@ describe('.updateArtifacts()', () => { expect(execSnapshots).toMatchSnapshot(); }); it('uses pipenv version from Pipfile', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig(dockerAdminConfig); + setAdminConfig(dockerAdminConfig); pipFileLock.default.pipenv.version = '==2020.8.13'; fs.readFile.mockResolvedValueOnce(JSON.stringify(pipFileLock) as any); const execSnapshots = mockExecAll(exec); @@ -176,14 +171,13 @@ describe('.updateArtifacts()', () => { packageFileName: 'Pipfile', updatedDeps: [], newPackageFileContent: 'some new content', - config: { ...config, ...dockerAdminConfig }, + config, }) ).not.toBeNull(); expect(execSnapshots).toMatchSnapshot(); }); it('uses pipenv version from Pipfile dev packages', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig(dockerAdminConfig); + setAdminConfig(dockerAdminConfig); pipFileLock.develop.pipenv.version = '==2020.8.13'; fs.readFile.mockResolvedValueOnce(JSON.stringify(pipFileLock) as any); const execSnapshots = mockExecAll(exec); @@ -196,14 +190,13 @@ describe('.updateArtifacts()', () => { packageFileName: 'Pipfile', updatedDeps: [], newPackageFileContent: 'some new content', - config: { ...config, ...dockerAdminConfig }, + config, }) ).not.toBeNull(); expect(execSnapshots).toMatchSnapshot(); }); it('uses pipenv version from config', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig(dockerAdminConfig); + setAdminConfig(dockerAdminConfig); pipFileLock.default.pipenv.version = '==2020.8.13'; fs.readFile.mockResolvedValueOnce(JSON.stringify(pipFileLock) as any); const execSnapshots = mockExecAll(exec); @@ -216,7 +209,7 @@ describe('.updateArtifacts()', () => { packageFileName: 'Pipfile', updatedDeps: [], newPackageFileContent: 'some new content', - config: { ...dockerAdminConfig, constraints: { pipenv: '==2020.1.1' } }, + config: { ...config, constraints: { pipenv: '==2020.1.1' } }, }) ).not.toBeNull(); expect(execSnapshots).toMatchSnapshot(); diff --git a/lib/manager/poetry/artifacts.spec.ts b/lib/manager/poetry/artifacts.spec.ts index 85a77bc6cf639a..08dd6c1288956d 100644 --- a/lib/manager/poetry/artifacts.spec.ts +++ b/lib/manager/poetry/artifacts.spec.ts @@ -6,8 +6,6 @@ import { loadFixture, mocked } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; import * as _datasource from '../../datasource'; -import { setExecConfig } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; import * as docker from '../../util/exec/docker'; import * as _env from '../../util/exec/env'; import * as _hostRules from '../../util/host-rules'; @@ -35,10 +33,9 @@ const adminConfig: RepoAdminConfig = { const config: UpdateArtifactsConfig = {}; describe('.updateArtifacts()', () => { - beforeEach(async () => { + beforeEach(() => { jest.resetAllMocks(); env.getChildProcessEnv.mockReturnValue(envMock.basic); - await setExecConfig(adminConfig as never); setAdminConfig(adminConfig); docker.resetPrefetchedImages(); }); @@ -133,8 +130,7 @@ describe('.updateArtifacts()', () => { expect(execSnapshots).toMatchSnapshot(); }); it('returns updated poetry.lock using docker', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readFile.mockResolvedValueOnce('[metadata]\n' as any); const execSnapshots = mockExecAll(exec); fs.readFile.mockReturnValueOnce('New poetry.lock' as any); @@ -159,8 +155,7 @@ describe('.updateArtifacts()', () => { expect(execSnapshots).toMatchSnapshot(); }); it('returns updated poetry.lock using docker (constraints)', async () => { - jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); - await setExecConfig({ ...adminConfig, binarySource: BinarySource.Docker }); + setAdminConfig({ ...adminConfig, binarySource: 'docker' }); fs.readFile.mockResolvedValueOnce( '[metadata]\npython-versions = "~2.7 || ^3.4"' as any ); diff --git a/lib/manager/pyenv/__snapshots__/extract.spec.ts.snap b/lib/manager/pyenv/__snapshots__/extract.spec.ts.snap new file mode 100644 index 00000000000000..a81be83ad9211c --- /dev/null +++ b/lib/manager/pyenv/__snapshots__/extract.spec.ts.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`manager/pyenv/extract extractPackageFile() returns a result 1`] = ` +Array [ + Object { + "currentValue": "3.7.1", + "datasource": "docker", + "depName": "python", + }, +] +`; + +exports[`manager/pyenv/extract extractPackageFile() skips non ranges 1`] = ` +Array [ + Object { + "currentValue": "latestn", + "datasource": "docker", + "depName": "python", + }, +] +`; + +exports[`manager/pyenv/extract extractPackageFile() supports ranges 1`] = ` +Array [ + Object { + "currentValue": "3.8", + "datasource": "docker", + "depName": "python", + }, +] +`; diff --git a/lib/manager/pyenv/extract.spec.ts b/lib/manager/pyenv/extract.spec.ts new file mode 100644 index 00000000000000..fb9a644b76d6ee --- /dev/null +++ b/lib/manager/pyenv/extract.spec.ts @@ -0,0 +1,19 @@ +import { getName } from '../../../test/util'; +import { extractPackageFile } from './extract'; + +describe(getName(), () => { + describe('extractPackageFile()', () => { + it('returns a result', () => { + const res = extractPackageFile('3.7.1\n'); + expect(res.deps).toMatchSnapshot(); + }); + it('supports ranges', () => { + const res = extractPackageFile('3.8\n'); + expect(res.deps).toMatchSnapshot(); + }); + it('skips non ranges', () => { + const res = extractPackageFile('latestn'); + expect(res.deps).toMatchSnapshot(); + }); + }); +}); diff --git a/lib/manager/pyenv/extract.ts b/lib/manager/pyenv/extract.ts new file mode 100644 index 00000000000000..8fee5607c1ff01 --- /dev/null +++ b/lib/manager/pyenv/extract.ts @@ -0,0 +1,11 @@ +import * as datasourceDocker from '../../datasource/docker'; +import type { PackageDependency, PackageFile } from '../types'; + +export function extractPackageFile(content: string): PackageFile { + const dep: PackageDependency = { + depName: 'python', + currentValue: content.trim(), + datasource: datasourceDocker.id, + }; + return { deps: [dep] }; +} diff --git a/lib/manager/pyenv/index.ts b/lib/manager/pyenv/index.ts new file mode 100644 index 00000000000000..7917135d678fc3 --- /dev/null +++ b/lib/manager/pyenv/index.ts @@ -0,0 +1,11 @@ +import { LANGUAGE_PYTHON } from '../../constants/languages'; +import * as dockerVersioning from '../../versioning/docker'; + +export { extractPackageFile } from './extract'; + +export const language = LANGUAGE_PYTHON; + +export const defaultConfig = { + fileMatch: ['(^|/).python-version$'], + versioning: dockerVersioning.id, +}; diff --git a/lib/manager/pyenv/readme.md b/lib/manager/pyenv/readme.md new file mode 100644 index 00000000000000..feadf30e9cf545 --- /dev/null +++ b/lib/manager/pyenv/readme.md @@ -0,0 +1 @@ +Simply keeps the `.python-version` file updated. diff --git a/lib/manager/regex/__fixtures__/gitlab-ci.yml b/lib/manager/regex/__fixtures__/gitlab-ci.yml new file mode 100644 index 00000000000000..55f074e222f467 --- /dev/null +++ b/lib/manager/regex/__fixtures__/gitlab-ci.yml @@ -0,0 +1,6 @@ +include: + - template: Auto-DevOps.gitlab-ci.yml +variables: + CHART_REPOSITORY_URL: "https://kubernetes-sigs.github.io/descheduler/" + CHART_NAME: "descheduler" + CHART_VERSION: 0.19.2 diff --git a/lib/manager/regex/__snapshots__/index.spec.ts.snap b/lib/manager/regex/__snapshots__/index.spec.ts.snap index cc1d94995c3fe9..9335afa0bd3892 100644 --- a/lib/manager/regex/__snapshots__/index.spec.ts.snap +++ b/lib/manager/regex/__snapshots__/index.spec.ts.snap @@ -229,6 +229,31 @@ Object { } `; +exports[`manager/regex/index extracts with combination strategy and registry url 1`] = ` +Object { + "datasourceTemplate": "helm", + "deps": Array [ + Object { + "currentValue": "0.19.2", + "datasource": "helm", + "depName": "descheduler", + "registryUrls": Array [ + "https://kubernetes-sigs.github.io/descheduler/", + ], + "replaceString": "CHART_VERSION: 0.19.2 +", + }, + ], + "matchStrings": Array [ + "CHART_VERSION: (?.*?) +", + "CHART_REPOSITORY_URL: \\"(?.*?)\\"", + "CHART_NAME: \\"(?.*?)\\"", + ], + "matchStringsStrategy": "combination", +} +`; + exports[`manager/regex/index extracts with recursive strategy and fail because of not sufficient regexes 1`] = `null`; exports[`manager/regex/index extracts with recursive strategy and fail because there is no match 1`] = `null`; diff --git a/lib/manager/regex/index.spec.ts b/lib/manager/regex/index.spec.ts index 30a45786ebcc1e..ed2bd5cc79082c 100644 --- a/lib/manager/regex/index.spec.ts +++ b/lib/manager/regex/index.spec.ts @@ -6,6 +6,7 @@ import { defaultConfig, extractPackageFile } from '.'; const dockerfileContent = loadFixture(`Dockerfile`); const ansibleYamlContent = loadFixture(`ansible.yml`); const exampleJsonContent = loadFixture(`example.json`); +const exampleGitlabCiYml = loadFixture(`gitlab-ci.yml`); describe(getName(), () => { it('has default config', () => { @@ -199,6 +200,24 @@ describe(getName(), () => { expect(res).toMatchSnapshot(); expect(res.deps).toHaveLength(1); }); + it('extracts with combination strategy and registry url', async () => { + const config: CustomExtractConfig = { + matchStringsStrategy: 'combination', + matchStrings: [ + 'CHART_VERSION: (?.*?)\n', + 'CHART_REPOSITORY_URL: "(?.*?)"', + 'CHART_NAME: "(?.*?)"', + ], + datasourceTemplate: 'helm', + }; + const res = await extractPackageFile( + exampleGitlabCiYml, + '.gitlab-ci.yml', + config + ); + expect(res).toMatchSnapshot(); + expect(res.deps).toHaveLength(1); + }); it('extracts with recursive strategy and single match', async () => { const config: CustomExtractConfig = { matchStrings: [ diff --git a/lib/manager/regex/index.ts b/lib/manager/regex/index.ts index 840870bfc4b782..250c735b18b5db 100644 --- a/lib/manager/regex/index.ts +++ b/lib/manager/regex/index.ts @@ -24,6 +24,8 @@ const validMatchFields = [ 'registryUrl', ]; +const mergeFields = ['registryUrls', ...validMatchFields]; + function regexMatchAll(regex: RegExp, content: string): RegExpMatchArray[] { const matches: RegExpMatchArray[] = []; let matchResult; @@ -85,7 +87,7 @@ function createDependency( function mergeDependency(deps: PackageDependency[]): PackageDependency { const result: PackageDependency = {}; deps.forEach((dep) => { - validMatchFields.forEach((field) => { + mergeFields.forEach((field) => { if (dep[field]) { result[field] = dep[field]; // save the line replaceString of the section which contains the current Value for a speed up lookup during the replace phase diff --git a/lib/manager/sbt/__fixtures__/dependency-file.scala b/lib/manager/sbt/__fixtures__/dependency-file.scala index d9046d12192cbb..bdd6f9fcd03d80 100644 --- a/lib/manager/sbt/__fixtures__/dependency-file.scala +++ b/lib/manager/sbt/__fixtures__/dependency-file.scala @@ -10,4 +10,11 @@ object Dependencies { val ujson = "com.example" %% "foo" % "0.7.1" lazy val abc = "com.abc" % "abc" % abcVersion + + val relatedDeps = Seq( + "com.abc" % "abc-a" % abcVersion, + "com.abc" % "abc-b" % abcVersion + ) + + val aloneDepInSeq = Seq("com.abc" % "abc-c" % abcVersion) } diff --git a/lib/manager/sbt/__snapshots__/extract.spec.ts.snap b/lib/manager/sbt/__snapshots__/extract.spec.ts.snap index c05ae39e1c013b..38bb61b805732a 100644 --- a/lib/manager/sbt/__snapshots__/extract.spec.ts.snap +++ b/lib/manager/sbt/__snapshots__/extract.spec.ts.snap @@ -69,6 +69,36 @@ Object { "https://repo.maven.apache.org/maven2", ], }, + Object { + "currentValue": "1.2.3", + "datasource": "sbt-package", + "depName": "com.abc:abc-a", + "groupName": "abcVersion for com.abc", + "lookupName": "com.abc:abc-a", + "registryUrls": Array [ + "https://repo.maven.apache.org/maven2", + ], + }, + Object { + "currentValue": "1.2.3", + "datasource": "sbt-package", + "depName": "com.abc:abc-b", + "groupName": "abcVersion for com.abc", + "lookupName": "com.abc:abc-b", + "registryUrls": Array [ + "https://repo.maven.apache.org/maven2", + ], + }, + Object { + "currentValue": "1.2.3", + "datasource": "sbt-package", + "depName": "com.abc:abc-c", + "groupName": "abcVersion for com.abc", + "lookupName": "com.abc:abc-c", + "registryUrls": Array [ + "https://repo.maven.apache.org/maven2", + ], + }, ], "packageFileVersion": undefined, } diff --git a/lib/manager/sbt/extract.ts b/lib/manager/sbt/extract.ts index 6b6b2dc5d2ec09..f83f81648cf3d2 100644 --- a/lib/manager/sbt/extract.ts +++ b/lib/manager/sbt/extract.ts @@ -86,6 +86,16 @@ const isVarDef = (str: string): boolean => str ); +const isVarSeqSingleLine = (str: string): boolean => + /^\s*(private\s*)?(lazy\s*)?val\s+[_a-zA-Z][_a-zA-Z0-9]*\s*=\s*Seq\(.*\).*\s*$/.test( + str + ); + +const isVarSeqMultipleLine = (str: string): boolean => + /^\s*(private\s*)?(lazy\s*)?val\s+[_a-zA-Z][_a-zA-Z0-9]*\s*=\s*Seq\(.*[^)]*.*$/.test( + str + ); + const getVarName = (str: string): string => str .replace(/^\s*(private\s*)?(lazy\s*)?val\s+/, '') @@ -231,6 +241,18 @@ function parseSbtLine( isMultiDeps = false; const url = getResolverUrl(line); registryUrls.push(url); + } else if (isVarSeqSingleLine(line)) { + isMultiDeps = false; + const depExpr = line.replace(/^.*Seq\(\s*/, '').replace(/\).*$/, ''); + dep = parseDepExpr(depExpr, { + ...ctx, + }); + } else if (isVarSeqMultipleLine(line)) { + isMultiDeps = true; + const depExpr = line.replace(/^.*Seq\(\s*/, ''); + dep = parseDepExpr(depExpr, { + ...ctx, + }); } else if (isVarDef(line)) { variables[getVarName(line)] = getVarInfo(line, ctx); } else if (isVarDependency(line)) { diff --git a/lib/manager/terraform/index.ts b/lib/manager/terraform/index.ts index 9b0a8b0d2050ee..17107d2b17b8bd 100644 --- a/lib/manager/terraform/index.ts +++ b/lib/manager/terraform/index.ts @@ -1,7 +1,9 @@ import * as hashicorpVersioning from '../../versioning/hashicorp'; +export { updateArtifacts } from './lockfile'; export { extractPackageFile } from './extract'; +export const supportsLockFileMaintenance = true; export const defaultConfig = { commitMessageTopic: 'Terraform {{managerData.terraformDependencyType}} {{depName}}', diff --git a/lib/manager/terraform/lockfile/__fixtures__/releaseBackendAzurerm_2_56_0.json b/lib/manager/terraform/lockfile/__fixtures__/releaseBackendAzurerm_2_56_0.json new file mode 100644 index 00000000000000..46b15368939320 --- /dev/null +++ b/lib/manager/terraform/lockfile/__fixtures__/releaseBackendAzurerm_2_56_0.json @@ -0,0 +1,24 @@ +{ + "name": "terraform-provider-azurerm", + "version": "2.56.0", + "shasums": "terraform-provider-azurerm_2.56.0_SHA256SUMS", + "shasums_signature": "terraform-provider-azurerm_2.56.0_SHA256SUMS.sig", + "builds": [ + { + "name": "terraform-provider-azurerm", + "version": "2.56.0", + "os": "darwin", + "arch": "amd64", + "filename": "terraform-provider-azurerm_2.56.0_darwin_amd64.zip", + "url": "https://releases.hashicorp.com/terraform-provider-azurerm/2.56.0/terraform-provider-azurerm_2.56.0_darwin_amd64.zip" + }, + { + "name": "terraform-provider-azurerm", + "version": "2.56.0", + "os": "linux", + "arch": "amd64", + "filename": "terraform-provider-azurerm_2.56.0_linux_amd64.zip", + "url": "https://releases.hashicorp.com/terraform-provider-azurerm/2.56.0/terraform-provider-azurerm_2.56.0_linux_amd64.zip" + } + ] +} diff --git a/lib/manager/terraform/lockfile/__fixtures__/test.zip b/lib/manager/terraform/lockfile/__fixtures__/test.zip new file mode 100644 index 00000000000000..7aa2446540b45e Binary files /dev/null and b/lib/manager/terraform/lockfile/__fixtures__/test.zip differ diff --git a/lib/manager/terraform/lockfile/__fixtures__/validLockfile.hcl b/lib/manager/terraform/lockfile/__fixtures__/validLockfile.hcl new file mode 100644 index 00000000000000..2c8d639cbf100a --- /dev/null +++ b/lib/manager/terraform/lockfile/__fixtures__/validLockfile.hcl @@ -0,0 +1,59 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "3.0.0" + constraints = "3.0.0" + hashes = [ + "h1:ULKfwySvQ4pDhy027ryRhLxDhg640wsojYc+7NHMFBU=", + "zh:25294510ae9c250502f2e37ac32b01017439735f098f82a1728772427626a2fd", + "zh:3b723e7772d47bd8cc11bea6e5d3e0b5e1df8398c0e7aaf510e3a8a54e0f1874", + "zh:4b7b73b86f4a0705d5d2a7f1d3ad3279706bdb3957a48f4a389c36918fba838e", + "zh:9e26cdc3be97e3001c253c0ca28c5c8ff2d5476373ca1beb849f3f3957ce7f1a", + "zh:9e73cf1304bf57968d3048d70c0b766d41497430a2a9a7a718a196f3a385106a", + "zh:a30b5b66facfbb2b02814e4cd33ca9899f9ade5bbf478f78c41d2fe789f0582a", + "zh:b06fb5da094db41cb5e430c95c988b73f32695e9f90f25499e926842dbd21b21", + "zh:c5a4ff607e9e9edee3fcd6d6666241fb532adf88ea1fe24f2aa1eb36845b3ca3", + "zh:df568a69087831c1780fac4395630a2cfb3cdf67b7dffbfe16bd78c64770bb75", + "zh:fce1b69dd673aace19508640b0b9b7eb1ef7e746d76cb846b49e7d52e0f5fb7e", + ] +} + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "2.50.0" + constraints = "~> 2.50" + hashes = [ + "h1:Vr6WUm88s9hXGkyVjHtHsP2Jmc2ypQXn6ww7dXtvk1M=", + "zh:0c0688d5a743248f8646d39eb3645a4ac19fd7523ba1b47072fa3fb03b92b1b0", + "zh:2beb3a55ee970f87a9292ae96d57134be8a03d0566117e7be0fe0d9c1267e4ea", + "zh:38091b463fbafe5756420ce34c87845c2a391fec0cded27bdcbbca28febad382", + "zh:4ba455da3b37ba8f8b03ff2781121d9c54d0bd8afd76dfe67593011c475dd73f", + "zh:5d32b9ed871b3c3b774dc69f1fe14cdf7c1fd63d12bb5f21aad4bfbf75e5ee3d", + "zh:6c80cf90a3fc1e17d9caf67cc558c2ff91f8b25e29fdf00942f67711895be5c0", + "zh:c0a53e3165407999d10de7aaa983485d42797433c60b5775791ae299121279ed", + "zh:dab51d6d76041505aeebf20111febe8616ec465ca31dfb7901f5f5c23a5af095", + "zh:e1ad6399f6a6d799002206ee4cb7b794dbb2533b8c3c14502a4419955ec96bff", + "zh:e98f1d178d1e111b3f3449e27d305ce263071226fad3d86272e1bd161c26fd43", + "zh:eb76ec000c9c49a0bf730370c8880f671597bc01f7b7401ab301df7124c049ec", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "2.2.1" + constraints = "~> 2.2" + hashes = [ + "h1:Zg1Bpi6vr7b0H6no8kVDfEucn5pvNALivdrVKVHarGs=", + "zh:072ce92b0138ee65df2e4e2e6e5f6632fa12a7e6453b91399bad89291855d426", + "zh:5731987fe61051515f449033e456ee55207caf17ef41096eb82247810585f53b", + "zh:6f18b10175708bb5839e1f2082dcc02651b876786cd54ec415a091f3821807c3", + "zh:7fa7737661380d18cba3cdc71c4ec6f2fd281b9d61112f6b48d06ca8bbf97771", + "zh:8466cb8fbb4de887b23039082a6e3dc85aeabce86dd808e2a7a65e4e1c51dbae", + "zh:888c63417701c13bbe785ab11dc690d4803e6a2156318cf188970b7b6400b99e", + "zh:a231df55d36fbad1a6705f5d3be4f7459a73ec76117d13f22aa83c10fc610278", + "zh:b62d9a4cd64a2d229070260f4abfef476ebbd7c5511b43e9cdccf23ce938f630", + "zh:b6bd1a325f909bb93f7c9bef00eb306bef1e406cbdf557901d755a3e7a4a5448", + "zh:b9f59afc23cc5567075f76313214baa1e5ce909325229e23c9a4666f7b26e7f7", + "zh:d040220c09b8d9d6bd937572bd5b14bc069af2b883185a873460530d8a1de6e6", + "zh:f254c1f943eb016ae07ebe91b23f813dc79f2064616c65f98c8f64ce23be90c4", + ] +} diff --git a/lib/manager/terraform/lockfile/__snapshots__/hash.spec.ts.snap b/lib/manager/terraform/lockfile/__snapshots__/hash.spec.ts.snap new file mode 100644 index 00000000000000..eb3e7b2238167d --- /dev/null +++ b/lib/manager/terraform/lockfile/__snapshots__/hash.spec.ts.snap @@ -0,0 +1,106 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`manager/terraform/lockfile/hash backend index throws error 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "releases.hashicorp.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://releases.hashicorp.com/terraform-provider-azurerm/2.56.0/index.json", + }, +] +`; + +exports[`manager/terraform/lockfile/hash fail to create hashes 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "releases.hashicorp.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://releases.hashicorp.com/terraform-provider-azurerm/2.56.0/index.json", + }, + Object { + "headers": Object { + "accept-encoding": "gzip, deflate, br", + "host": "releases.hashicorp.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://releases.hashicorp.com/terraform-provider-azurerm/2.56.0/terraform-provider-azurerm_2.56.0_darwin_amd64.zip", + }, + Object { + "headers": Object { + "accept-encoding": "gzip, deflate, br", + "host": "releases.hashicorp.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://releases.hashicorp.com/terraform-provider-azurerm/2.56.0/terraform-provider-azurerm_2.56.0_linux_amd64.zip", + }, +] +`; + +exports[`manager/terraform/lockfile/hash full walkthrough 1`] = `Array []`; + +exports[`manager/terraform/lockfile/hash full walkthrough 2`] = ` +Array [ + "h1:I2F2atKZqKEOYk1tTLe15Llf9rVqxz48ZL1eZB9g8zM=", + "h1:I2F2atKZqKEOYk1tTLe15Llf9rVqxz48ZL1eZB9g8zM=", +] +`; + +exports[`manager/terraform/lockfile/hash full walkthrough 3`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "releases.hashicorp.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://releases.hashicorp.com/terraform-provider-azurerm/2.56.0/index.json", + }, + Object { + "headers": Object { + "accept-encoding": "gzip, deflate, br", + "host": "releases.hashicorp.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://releases.hashicorp.com/terraform-provider-azurerm/2.56.0/terraform-provider-azurerm_2.56.0_darwin_amd64.zip", + }, + Object { + "headers": Object { + "accept-encoding": "gzip, deflate, br", + "host": "releases.hashicorp.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://releases.hashicorp.com/terraform-provider-azurerm/2.56.0/terraform-provider-azurerm_2.56.0_linux_amd64.zip", + }, +] +`; + +exports[`manager/terraform/lockfile/hash return null if requesting a version which is not available 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "releases.hashicorp.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://releases.hashicorp.com/terraform-provider-azurerm/2.59.0/index.json", + }, +] +`; diff --git a/lib/manager/terraform/lockfile/__snapshots__/index.spec.ts.snap b/lib/manager/terraform/lockfile/__snapshots__/index.spec.ts.snap new file mode 100644 index 00000000000000..5061dc2f117222 --- /dev/null +++ b/lib/manager/terraform/lockfile/__snapshots__/index.spec.ts.snap @@ -0,0 +1,269 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`manager/terraform/lockfile/index do full lock file maintenance 1`] = ` +Object { + "contents": "# This file is maintained automatically by \\"terraform init\\". +# Manual edits may be lost in future updates. + +provider \\"registry.terraform.io/hashicorp/aws\\" { + version = \\"3.0.0\\" + constraints = \\"3.0.0\\" + hashes = [ + \\"h1:ULKfwySvQ4pDhy027ryRhLxDhg640wsojYc+7NHMFBU=\\", + \\"zh:25294510ae9c250502f2e37ac32b01017439735f098f82a1728772427626a2fd\\", + \\"zh:3b723e7772d47bd8cc11bea6e5d3e0b5e1df8398c0e7aaf510e3a8a54e0f1874\\", + \\"zh:4b7b73b86f4a0705d5d2a7f1d3ad3279706bdb3957a48f4a389c36918fba838e\\", + \\"zh:9e26cdc3be97e3001c253c0ca28c5c8ff2d5476373ca1beb849f3f3957ce7f1a\\", + \\"zh:9e73cf1304bf57968d3048d70c0b766d41497430a2a9a7a718a196f3a385106a\\", + \\"zh:a30b5b66facfbb2b02814e4cd33ca9899f9ade5bbf478f78c41d2fe789f0582a\\", + \\"zh:b06fb5da094db41cb5e430c95c988b73f32695e9f90f25499e926842dbd21b21\\", + \\"zh:c5a4ff607e9e9edee3fcd6d6666241fb532adf88ea1fe24f2aa1eb36845b3ca3\\", + \\"zh:df568a69087831c1780fac4395630a2cfb3cdf67b7dffbfe16bd78c64770bb75\\", + \\"zh:fce1b69dd673aace19508640b0b9b7eb1ef7e746d76cb846b49e7d52e0f5fb7e\\", + ] +} + +provider \\"registry.terraform.io/hashicorp/azurerm\\" { + version = \\"2.56.0\\" + constraints = \\"~> 2.50\\" + hashes = [ + \\"h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=\\", + \\"h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=\\", + ] +} + +provider \\"registry.terraform.io/hashicorp/random\\" { + version = \\"2.2.2\\" + constraints = \\"~> 2.2\\" + hashes = [ + \\"h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=\\", + \\"h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=\\", + ] +} +", + "name": ".terraform.lock.hcl", +} +`; + +exports[`manager/terraform/lockfile/index do full lock file maintenance 2`] = ` +Array [ + Array [ + "hashicorp/azurerm", + "2.56.0", + ], + Array [ + "hashicorp/random", + "2.2.2", + ], +] +`; + +exports[`manager/terraform/lockfile/index do full lock file maintenance without necessary changes 1`] = `Array []`; + +exports[`manager/terraform/lockfile/index return null if hashing fails 1`] = ` +Array [ + Array [ + "hashicorp/azurerm", + "2.56.0", + ], + Array [ + "hashicorp/random", + "2.2.2", + ], +] +`; + +exports[`manager/terraform/lockfile/index update single dependency with exact constraint 1`] = ` +Object { + "contents": "# This file is maintained automatically by \\"terraform init\\". +# Manual edits may be lost in future updates. + +provider \\"registry.terraform.io/hashicorp/aws\\" { + version = \\"3.36.0\\" + constraints = \\"3.36.0\\" + hashes = [ + \\"h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=\\", + \\"h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=\\", + ] +} + +provider \\"registry.terraform.io/hashicorp/azurerm\\" { + version = \\"2.50.0\\" + constraints = \\"~> 2.50\\" + hashes = [ + \\"h1:Vr6WUm88s9hXGkyVjHtHsP2Jmc2ypQXn6ww7dXtvk1M=\\", + \\"zh:0c0688d5a743248f8646d39eb3645a4ac19fd7523ba1b47072fa3fb03b92b1b0\\", + \\"zh:2beb3a55ee970f87a9292ae96d57134be8a03d0566117e7be0fe0d9c1267e4ea\\", + \\"zh:38091b463fbafe5756420ce34c87845c2a391fec0cded27bdcbbca28febad382\\", + \\"zh:4ba455da3b37ba8f8b03ff2781121d9c54d0bd8afd76dfe67593011c475dd73f\\", + \\"zh:5d32b9ed871b3c3b774dc69f1fe14cdf7c1fd63d12bb5f21aad4bfbf75e5ee3d\\", + \\"zh:6c80cf90a3fc1e17d9caf67cc558c2ff91f8b25e29fdf00942f67711895be5c0\\", + \\"zh:c0a53e3165407999d10de7aaa983485d42797433c60b5775791ae299121279ed\\", + \\"zh:dab51d6d76041505aeebf20111febe8616ec465ca31dfb7901f5f5c23a5af095\\", + \\"zh:e1ad6399f6a6d799002206ee4cb7b794dbb2533b8c3c14502a4419955ec96bff\\", + \\"zh:e98f1d178d1e111b3f3449e27d305ce263071226fad3d86272e1bd161c26fd43\\", + \\"zh:eb76ec000c9c49a0bf730370c8880f671597bc01f7b7401ab301df7124c049ec\\", + ] +} + +provider \\"registry.terraform.io/hashicorp/random\\" { + version = \\"2.2.1\\" + constraints = \\"~> 2.2\\" + hashes = [ + \\"h1:Zg1Bpi6vr7b0H6no8kVDfEucn5pvNALivdrVKVHarGs=\\", + \\"zh:072ce92b0138ee65df2e4e2e6e5f6632fa12a7e6453b91399bad89291855d426\\", + \\"zh:5731987fe61051515f449033e456ee55207caf17ef41096eb82247810585f53b\\", + \\"zh:6f18b10175708bb5839e1f2082dcc02651b876786cd54ec415a091f3821807c3\\", + \\"zh:7fa7737661380d18cba3cdc71c4ec6f2fd281b9d61112f6b48d06ca8bbf97771\\", + \\"zh:8466cb8fbb4de887b23039082a6e3dc85aeabce86dd808e2a7a65e4e1c51dbae\\", + \\"zh:888c63417701c13bbe785ab11dc690d4803e6a2156318cf188970b7b6400b99e\\", + \\"zh:a231df55d36fbad1a6705f5d3be4f7459a73ec76117d13f22aa83c10fc610278\\", + \\"zh:b62d9a4cd64a2d229070260f4abfef476ebbd7c5511b43e9cdccf23ce938f630\\", + \\"zh:b6bd1a325f909bb93f7c9bef00eb306bef1e406cbdf557901d755a3e7a4a5448\\", + \\"zh:b9f59afc23cc5567075f76313214baa1e5ce909325229e23c9a4666f7b26e7f7\\", + \\"zh:d040220c09b8d9d6bd937572bd5b14bc069af2b883185a873460530d8a1de6e6\\", + \\"zh:f254c1f943eb016ae07ebe91b23f813dc79f2064616c65f98c8f64ce23be90c4\\", + ] +} +", + "name": ".terraform.lock.hcl", +} +`; + +exports[`manager/terraform/lockfile/index update single dependency with exact constraint 2`] = ` +Array [ + Array [ + "hashicorp/aws", + "3.36.0", + ], +] +`; + +exports[`manager/terraform/lockfile/index update single dependency with range constraint and major update 1`] = ` +Object { + "contents": "# This file is maintained automatically by \\"terraform init\\". +# Manual edits may be lost in future updates. + +provider \\"registry.terraform.io/hashicorp/aws\\" { + version = \\"3.0.0\\" + constraints = \\"3.0.0\\" + hashes = [ + \\"h1:ULKfwySvQ4pDhy027ryRhLxDhg640wsojYc+7NHMFBU=\\", + \\"zh:25294510ae9c250502f2e37ac32b01017439735f098f82a1728772427626a2fd\\", + \\"zh:3b723e7772d47bd8cc11bea6e5d3e0b5e1df8398c0e7aaf510e3a8a54e0f1874\\", + \\"zh:4b7b73b86f4a0705d5d2a7f1d3ad3279706bdb3957a48f4a389c36918fba838e\\", + \\"zh:9e26cdc3be97e3001c253c0ca28c5c8ff2d5476373ca1beb849f3f3957ce7f1a\\", + \\"zh:9e73cf1304bf57968d3048d70c0b766d41497430a2a9a7a718a196f3a385106a\\", + \\"zh:a30b5b66facfbb2b02814e4cd33ca9899f9ade5bbf478f78c41d2fe789f0582a\\", + \\"zh:b06fb5da094db41cb5e430c95c988b73f32695e9f90f25499e926842dbd21b21\\", + \\"zh:c5a4ff607e9e9edee3fcd6d6666241fb532adf88ea1fe24f2aa1eb36845b3ca3\\", + \\"zh:df568a69087831c1780fac4395630a2cfb3cdf67b7dffbfe16bd78c64770bb75\\", + \\"zh:fce1b69dd673aace19508640b0b9b7eb1ef7e746d76cb846b49e7d52e0f5fb7e\\", + ] +} + +provider \\"registry.terraform.io/hashicorp/azurerm\\" { + version = \\"2.50.0\\" + constraints = \\"~> 2.50\\" + hashes = [ + \\"h1:Vr6WUm88s9hXGkyVjHtHsP2Jmc2ypQXn6ww7dXtvk1M=\\", + \\"zh:0c0688d5a743248f8646d39eb3645a4ac19fd7523ba1b47072fa3fb03b92b1b0\\", + \\"zh:2beb3a55ee970f87a9292ae96d57134be8a03d0566117e7be0fe0d9c1267e4ea\\", + \\"zh:38091b463fbafe5756420ce34c87845c2a391fec0cded27bdcbbca28febad382\\", + \\"zh:4ba455da3b37ba8f8b03ff2781121d9c54d0bd8afd76dfe67593011c475dd73f\\", + \\"zh:5d32b9ed871b3c3b774dc69f1fe14cdf7c1fd63d12bb5f21aad4bfbf75e5ee3d\\", + \\"zh:6c80cf90a3fc1e17d9caf67cc558c2ff91f8b25e29fdf00942f67711895be5c0\\", + \\"zh:c0a53e3165407999d10de7aaa983485d42797433c60b5775791ae299121279ed\\", + \\"zh:dab51d6d76041505aeebf20111febe8616ec465ca31dfb7901f5f5c23a5af095\\", + \\"zh:e1ad6399f6a6d799002206ee4cb7b794dbb2533b8c3c14502a4419955ec96bff\\", + \\"zh:e98f1d178d1e111b3f3449e27d305ce263071226fad3d86272e1bd161c26fd43\\", + \\"zh:eb76ec000c9c49a0bf730370c8880f671597bc01f7b7401ab301df7124c049ec\\", + ] +} + +provider \\"registry.terraform.io/hashicorp/random\\" { + version = \\"3.1.0\\" + constraints = \\"~> 3.0\\" + hashes = [ + \\"h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=\\", + \\"h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=\\", + ] +} +", + "name": ".terraform.lock.hcl", +} +`; + +exports[`manager/terraform/lockfile/index update single dependency with range constraint and major update 2`] = ` +Array [ + Array [ + "hashicorp/random", + "3.1.0", + ], +] +`; + +exports[`manager/terraform/lockfile/index update single dependency with range constraint and minor update 1`] = ` +Object { + "contents": "# This file is maintained automatically by \\"terraform init\\". +# Manual edits may be lost in future updates. + +provider \\"registry.terraform.io/hashicorp/aws\\" { + version = \\"3.0.0\\" + constraints = \\"3.0.0\\" + hashes = [ + \\"h1:ULKfwySvQ4pDhy027ryRhLxDhg640wsojYc+7NHMFBU=\\", + \\"zh:25294510ae9c250502f2e37ac32b01017439735f098f82a1728772427626a2fd\\", + \\"zh:3b723e7772d47bd8cc11bea6e5d3e0b5e1df8398c0e7aaf510e3a8a54e0f1874\\", + \\"zh:4b7b73b86f4a0705d5d2a7f1d3ad3279706bdb3957a48f4a389c36918fba838e\\", + \\"zh:9e26cdc3be97e3001c253c0ca28c5c8ff2d5476373ca1beb849f3f3957ce7f1a\\", + \\"zh:9e73cf1304bf57968d3048d70c0b766d41497430a2a9a7a718a196f3a385106a\\", + \\"zh:a30b5b66facfbb2b02814e4cd33ca9899f9ade5bbf478f78c41d2fe789f0582a\\", + \\"zh:b06fb5da094db41cb5e430c95c988b73f32695e9f90f25499e926842dbd21b21\\", + \\"zh:c5a4ff607e9e9edee3fcd6d6666241fb532adf88ea1fe24f2aa1eb36845b3ca3\\", + \\"zh:df568a69087831c1780fac4395630a2cfb3cdf67b7dffbfe16bd78c64770bb75\\", + \\"zh:fce1b69dd673aace19508640b0b9b7eb1ef7e746d76cb846b49e7d52e0f5fb7e\\", + ] +} + +provider \\"registry.terraform.io/hashicorp/azurerm\\" { + version = \\"2.56.0\\" + constraints = \\"~> 2.50\\" + hashes = [ + \\"h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=\\", + \\"h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=\\", + ] +} + +provider \\"registry.terraform.io/hashicorp/random\\" { + version = \\"2.2.1\\" + constraints = \\"~> 2.2\\" + hashes = [ + \\"h1:Zg1Bpi6vr7b0H6no8kVDfEucn5pvNALivdrVKVHarGs=\\", + \\"zh:072ce92b0138ee65df2e4e2e6e5f6632fa12a7e6453b91399bad89291855d426\\", + \\"zh:5731987fe61051515f449033e456ee55207caf17ef41096eb82247810585f53b\\", + \\"zh:6f18b10175708bb5839e1f2082dcc02651b876786cd54ec415a091f3821807c3\\", + \\"zh:7fa7737661380d18cba3cdc71c4ec6f2fd281b9d61112f6b48d06ca8bbf97771\\", + \\"zh:8466cb8fbb4de887b23039082a6e3dc85aeabce86dd808e2a7a65e4e1c51dbae\\", + \\"zh:888c63417701c13bbe785ab11dc690d4803e6a2156318cf188970b7b6400b99e\\", + \\"zh:a231df55d36fbad1a6705f5d3be4f7459a73ec76117d13f22aa83c10fc610278\\", + \\"zh:b62d9a4cd64a2d229070260f4abfef476ebbd7c5511b43e9cdccf23ce938f630\\", + \\"zh:b6bd1a325f909bb93f7c9bef00eb306bef1e406cbdf557901d755a3e7a4a5448\\", + \\"zh:b9f59afc23cc5567075f76313214baa1e5ce909325229e23c9a4666f7b26e7f7\\", + \\"zh:d040220c09b8d9d6bd937572bd5b14bc069af2b883185a873460530d8a1de6e6\\", + \\"zh:f254c1f943eb016ae07ebe91b23f813dc79f2064616c65f98c8f64ce23be90c4\\", + ] +} +", + "name": ".terraform.lock.hcl", +} +`; + +exports[`manager/terraform/lockfile/index update single dependency with range constraint and minor update 2`] = ` +Array [ + Array [ + "hashicorp/azurerm", + "2.56.0", + ], +] +`; diff --git a/lib/manager/terraform/lockfile/__snapshots__/util.spec.ts.snap b/lib/manager/terraform/lockfile/__snapshots__/util.spec.ts.snap new file mode 100644 index 00000000000000..c17268a2dcfc05 --- /dev/null +++ b/lib/manager/terraform/lockfile/__snapshots__/util.spec.ts.snap @@ -0,0 +1,102 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`manager/terraform/lockfile/util extractLocks() extracts 1`] = ` +Array [ + Object { + "constraints": "3.0.0", + "hashes": Array [ + "h1:ULKfwySvQ4pDhy027ryRhLxDhg640wsojYc+7NHMFBU=", + "zh:25294510ae9c250502f2e37ac32b01017439735f098f82a1728772427626a2fd", + "zh:3b723e7772d47bd8cc11bea6e5d3e0b5e1df8398c0e7aaf510e3a8a54e0f1874", + "zh:4b7b73b86f4a0705d5d2a7f1d3ad3279706bdb3957a48f4a389c36918fba838e", + "zh:9e26cdc3be97e3001c253c0ca28c5c8ff2d5476373ca1beb849f3f3957ce7f1a", + "zh:9e73cf1304bf57968d3048d70c0b766d41497430a2a9a7a718a196f3a385106a", + "zh:a30b5b66facfbb2b02814e4cd33ca9899f9ade5bbf478f78c41d2fe789f0582a", + "zh:b06fb5da094db41cb5e430c95c988b73f32695e9f90f25499e926842dbd21b21", + "zh:c5a4ff607e9e9edee3fcd6d6666241fb532adf88ea1fe24f2aa1eb36845b3ca3", + "zh:df568a69087831c1780fac4395630a2cfb3cdf67b7dffbfe16bd78c64770bb75", + "zh:fce1b69dd673aace19508640b0b9b7eb1ef7e746d76cb846b49e7d52e0f5fb7e", + ], + "lineNumbers": Object { + "block": Object { + "end": 21, + "start": 3, + }, + "constraint": 2, + "hashes": Object { + "end": 14, + "start": 4, + }, + "version": 1, + }, + "lookupName": "hashicorp/aws", + "registryUrl": "registry.terraform.io", + "version": "3.0.0", + }, + Object { + "constraints": "~> 2.50", + "hashes": Array [ + "h1:Vr6WUm88s9hXGkyVjHtHsP2Jmc2ypQXn6ww7dXtvk1M=", + "zh:0c0688d5a743248f8646d39eb3645a4ac19fd7523ba1b47072fa3fb03b92b1b0", + "zh:2beb3a55ee970f87a9292ae96d57134be8a03d0566117e7be0fe0d9c1267e4ea", + "zh:38091b463fbafe5756420ce34c87845c2a391fec0cded27bdcbbca28febad382", + "zh:4ba455da3b37ba8f8b03ff2781121d9c54d0bd8afd76dfe67593011c475dd73f", + "zh:5d32b9ed871b3c3b774dc69f1fe14cdf7c1fd63d12bb5f21aad4bfbf75e5ee3d", + "zh:6c80cf90a3fc1e17d9caf67cc558c2ff91f8b25e29fdf00942f67711895be5c0", + "zh:c0a53e3165407999d10de7aaa983485d42797433c60b5775791ae299121279ed", + "zh:dab51d6d76041505aeebf20111febe8616ec465ca31dfb7901f5f5c23a5af095", + "zh:e1ad6399f6a6d799002206ee4cb7b794dbb2533b8c3c14502a4419955ec96bff", + "zh:e98f1d178d1e111b3f3449e27d305ce263071226fad3d86272e1bd161c26fd43", + "zh:eb76ec000c9c49a0bf730370c8880f671597bc01f7b7401ab301df7124c049ec", + ], + "lineNumbers": Object { + "block": Object { + "end": 40, + "start": 21, + }, + "constraint": 2, + "hashes": Object { + "end": 15, + "start": 4, + }, + "version": 1, + }, + "lookupName": "hashicorp/azurerm", + "registryUrl": "registry.terraform.io", + "version": "2.50.0", + }, + Object { + "constraints": "~> 2.2", + "hashes": Array [ + "h1:Zg1Bpi6vr7b0H6no8kVDfEucn5pvNALivdrVKVHarGs=", + "zh:072ce92b0138ee65df2e4e2e6e5f6632fa12a7e6453b91399bad89291855d426", + "zh:5731987fe61051515f449033e456ee55207caf17ef41096eb82247810585f53b", + "zh:6f18b10175708bb5839e1f2082dcc02651b876786cd54ec415a091f3821807c3", + "zh:7fa7737661380d18cba3cdc71c4ec6f2fd281b9d61112f6b48d06ca8bbf97771", + "zh:8466cb8fbb4de887b23039082a6e3dc85aeabce86dd808e2a7a65e4e1c51dbae", + "zh:888c63417701c13bbe785ab11dc690d4803e6a2156318cf188970b7b6400b99e", + "zh:a231df55d36fbad1a6705f5d3be4f7459a73ec76117d13f22aa83c10fc610278", + "zh:b62d9a4cd64a2d229070260f4abfef476ebbd7c5511b43e9cdccf23ce938f630", + "zh:b6bd1a325f909bb93f7c9bef00eb306bef1e406cbdf557901d755a3e7a4a5448", + "zh:b9f59afc23cc5567075f76313214baa1e5ce909325229e23c9a4666f7b26e7f7", + "zh:d040220c09b8d9d6bd937572bd5b14bc069af2b883185a873460530d8a1de6e6", + "zh:f254c1f943eb016ae07ebe91b23f813dc79f2064616c65f98c8f64ce23be90c4", + ], + "lineNumbers": Object { + "block": Object { + "end": 59, + "start": 40, + }, + "constraint": 2, + "hashes": Object { + "end": 16, + "start": 4, + }, + "version": 1, + }, + "lookupName": "hashicorp/random", + "registryUrl": "registry.terraform.io", + "version": "2.2.1", + }, +] +`; diff --git a/lib/manager/terraform/lockfile/hash.spec.ts b/lib/manager/terraform/lockfile/hash.spec.ts new file mode 100644 index 00000000000000..45a1b5f6358817 --- /dev/null +++ b/lib/manager/terraform/lockfile/hash.spec.ts @@ -0,0 +1,111 @@ +import { createReadStream } from 'fs'; +import { DirectoryResult, dir } from 'tmp-promise'; +import * as httpMock from '../../../../test/http-mock'; +import { + getFixturePath, + getName, + loadFixture, + logger, +} from '../../../../test/util'; +import { setAdminConfig } from '../../../config/admin'; +import { TerraformProviderDatasource } from '../../../datasource/terraform-provider'; +import { Logger } from '../../../logger/types'; +import { createHashes } from './hash'; + +const releaseBackendUrl = TerraformProviderDatasource.defaultRegistryUrls[1]; +const releaseBackendAzurerm = loadFixture('releaseBackendAzurerm_2_56_0.json'); + +const log = logger.logger as jest.Mocked; + +describe(getName(), () => { + let cacheDir: DirectoryResult; + + beforeAll(async () => { + cacheDir = await dir({ unsafeCleanup: true }); + setAdminConfig({ cacheDir: cacheDir.path }); + }); + + beforeEach(() => jest.resetAllMocks()); + + afterAll(() => cacheDir.cleanup()); + + it('returns null if a non hashicorp release is found ', async () => { + const result = await createHashes('test/gitlab', '2.56.0'); + expect(result).toBeNull(); + }); + + it('return null if requesting a version which is not available', async () => { + httpMock + .scope(releaseBackendUrl) + .get('/terraform-provider-azurerm/2.59.0/index.json') + .reply(403, ''); + + const result = await createHashes('hashicorp/azurerm', '2.59.0'); + expect(result).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); + }); + + it('backend index throws error', async () => { + httpMock + .scope(releaseBackendUrl) + .get('/terraform-provider-azurerm/2.56.0/index.json') + .replyWithError(''); + + const result = await createHashes('hashicorp/azurerm', '2.56.0'); + expect(result).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); + }); + + it('fail to create hashes', async () => { + const readStreamLinux = createReadStream( + getFixturePath('releaseBackendAzurerm_2_56_0.json') + ); + const readStreamDarwin = createReadStream( + getFixturePath('releaseBackendAzurerm_2_56_0.json') + ); + httpMock + .scope(releaseBackendUrl) + .get('/terraform-provider-azurerm/2.56.0/index.json') + .reply(200, releaseBackendAzurerm) + .get( + '/terraform-provider-azurerm/2.56.0/terraform-provider-azurerm_2.56.0_linux_amd64.zip' + ) + .reply(200, readStreamLinux) + .get( + '/terraform-provider-azurerm/2.56.0/terraform-provider-azurerm_2.56.0_darwin_amd64.zip' + ) + .reply(200, readStreamDarwin); + + const result = await createHashes('hashicorp/azurerm', '2.56.0'); + expect(result).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); + }); + + it('full walkthrough', async () => { + const readStreamLinux = createReadStream( + 'lib/manager/terraform/lockfile/__fixtures__/test.zip' + ); + const readStreamDarwin = createReadStream( + 'lib/manager/terraform/lockfile/__fixtures__/test.zip' + ); + httpMock + .scope(releaseBackendUrl) + .get('/terraform-provider-azurerm/2.56.0/index.json') + .reply(200, releaseBackendAzurerm) + .get( + '/terraform-provider-azurerm/2.56.0/terraform-provider-azurerm_2.56.0_linux_amd64.zip' + ) + .reply(200, readStreamLinux) + .get( + '/terraform-provider-azurerm/2.56.0/terraform-provider-azurerm_2.56.0_darwin_amd64.zip' + ) + .reply(200, readStreamDarwin); + + const result = await createHashes('hashicorp/azurerm', '2.56.0'); + expect(log.error.mock.calls).toMatchSnapshot(); + expect(result).not.toBeNull(); + expect(result).toBeArrayOfSize(2); + expect(result).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); + }); +}); diff --git a/lib/manager/terraform/lockfile/hash.ts b/lib/manager/terraform/lockfile/hash.ts new file mode 100644 index 00000000000000..7bb24a491cd284 --- /dev/null +++ b/lib/manager/terraform/lockfile/hash.ts @@ -0,0 +1,169 @@ +import crypto from 'crypto'; +import extract from 'extract-zip'; +import pMap from 'p-map'; +import { join } from 'upath'; +import { TerraformProviderDatasource } from '../../../datasource/terraform-provider'; +import type { + TerraformBuild, + VersionDetailResponse, +} from '../../../datasource/terraform-provider/types'; +import { logger } from '../../../logger'; +import * as packageCache from '../../../util/cache/package'; +import * as fs from '../../../util/fs'; +import { Http } from '../../../util/http'; +import { getCacheDir, repositoryRegex } from './util'; + +const http = new Http(TerraformProviderDatasource.id); +const hashCacheTTL = 10080; // in seconds == 1 week + +export async function hashFiles(files: string[]): Promise { + const rootHash = crypto.createHash('sha256'); + + for (const file of files) { + // build for every file a line looking like "aaaaaaaaaaaaaaa file.txt\n" + const hash = crypto.createHash('sha256'); + + // a sha256sum displayed as lowercase hex string to root hash + const fileBuffer = await fs.readFile(file); + hash.update(fileBuffer); + hash.end(); + const data = hash.read(); + rootHash.update(data.toString('hex')); + + // add double space, the filename and a new line char + rootHash.update(' '); + const fileName = file.replace(/^.*[\\/]/, ''); + rootHash.update(fileName); + rootHash.update('\n'); + } + + rootHash.end(); + const rootData = rootHash.read(); + const result: string = rootData.toString('base64'); + return result; +} + +export async function hashOfZipContent( + zipFilePath: string, + extractPath: string +): Promise { + await extract(zipFilePath, { dir: extractPath }); + const files = await fs.readdir(extractPath); + // the h1 hashing algorithms requires that the files are sorted by filename + const sortedFiles = files.sort((a, b) => a.localeCompare(b)); + const filesWithPath = sortedFiles.map((file) => `${extractPath}/${file}`); + + const result = await hashFiles(filesWithPath); + + // delete extracted files + await fs.rm(extractPath, { recursive: true }); + + return result; +} + +async function getReleaseBackendIndex( + backendLookUpName: string, + version: string +): Promise { + return ( + await http.getJson( + `${TerraformProviderDatasource.defaultRegistryUrls[1]}/${backendLookUpName}/${version}/index.json` + ) + ).body; +} + +export async function calculateHashes( + builds: TerraformBuild[] +): Promise { + const cacheDir = await getCacheDir(); + + // for each build download ZIP, extract content and generate hash for all containing files + const hashes = await pMap( + builds, + async (build) => { + const downloadFileName = join(cacheDir, build.filename); + const extractPath = join(cacheDir, 'extract', build.filename); + logger.trace( + `Downloading archive and generating hash for ${build.name}-${build.version}...` + ); + const readStream = http.stream(build.url); + const writeStream = fs.createWriteStream(downloadFileName); + + let hash = null; + try { + await fs.pipeline(readStream, writeStream); + + hash = await hashOfZipContent(downloadFileName, extractPath); + logger.trace( + { hash }, + `Generated hash for ${build.name}-${build.version}` + ); + } catch (err) { + /* istanbul ignore next */ + logger.error({ err, build }, 'write stream error'); + } finally { + // delete zip file + await fs.unlink(downloadFileName); + } + return hash; + }, + { concurrency: 4 } // allow to look up 4 builds for this version in parallel + ); + return hashes; +} + +export async function createHashes( + repository: string, + version: string +): Promise { + // check cache for hashes + const repositoryRegexResult = repositoryRegex.exec(repository); + if (!repositoryRegexResult) { + // non hashicorp builds are not supported at the moment + return null; + } + const lookupName = repositoryRegexResult.groups.lookupName; + const backendLookUpName = `terraform-provider-${lookupName}`; + + const cacheKey = `${TerraformProviderDatasource.defaultRegistryUrls[1]}/${repository}/${lookupName}-${version}`; + const cachedRelease = await packageCache.get( + 'terraform-provider-release', + cacheKey + ); + // istanbul ignore if + if (cachedRelease) { + return cachedRelease; + } + let versionReleaseBackend: VersionDetailResponse; + try { + versionReleaseBackend = await getReleaseBackendIndex( + backendLookUpName, + version + ); + } catch (err) { + logger.debug( + { err, backendLookUpName, version }, + `Failed to retrieve builds for ${backendLookUpName} ${version}` + ); + return null; + } + + const builds = versionReleaseBackend.builds; + const hashes = await calculateHashes(builds); + + // if a hash could not be produced skip caching and return null + if (hashes.some((value) => value == null)) { + return null; + } + + // sorting the hash alphabetically as terraform does this as well + const sortedHashes = hashes.sort().map((hash) => `h1:${hash}`); + // save to cache + await packageCache.set( + 'terraform-provider-release', + cacheKey, + sortedHashes, + hashCacheTTL + ); + return sortedHashes; +} diff --git a/lib/manager/terraform/lockfile/index.spec.ts b/lib/manager/terraform/lockfile/index.spec.ts new file mode 100644 index 00000000000000..b8968958685cf0 --- /dev/null +++ b/lib/manager/terraform/lockfile/index.spec.ts @@ -0,0 +1,374 @@ +import { join } from 'upath'; +import { fs, getName, loadFixture, mocked } from '../../../../test/util'; +import { setAdminConfig } from '../../../config/admin'; +import { getPkgReleases } from '../../../datasource'; +import type { UpdateArtifactsConfig } from '../../types'; +import * as hash from './hash'; +import { updateArtifacts } from './index'; + +// auto-mock fs +jest.mock('../../../util/fs'); +jest.mock('./hash'); +jest.mock('../../../datasource'); + +const config = { + constraints: {}, +}; + +const adminConfig = { + // `join` fixes Windows CI + localDir: join('/tmp/github/some/repo'), + cacheDir: join('/tmp/renovate/cache'), +}; + +const validLockfile = loadFixture('validLockfile.hcl'); + +const mockHash = mocked(hash).createHashes; +const mockGetPkgReleases = getPkgReleases as jest.MockedFunction< + typeof getPkgReleases +>; + +describe(getName(), () => { + beforeEach(() => { + jest.resetAllMocks(); + jest.resetModules(); + setAdminConfig(adminConfig); + }); + + afterEach(() => { + delete process.env.RENOVATE_X_TERRAFORM_LOCK_FILE; + }); + + it('returns null if no .terraform.lock.hcl found', async () => { + fs.readLocalFile.mockResolvedValueOnce(null); + + process.env.RENOVATE_X_TERRAFORM_LOCK_FILE = 'test'; + + expect( + await updateArtifacts({ + packageFileName: 'main.tf', + updatedDeps: ['aws'], + newPackageFileContent: '', + config, + }) + ).toBeNull(); + }); + + it('returns null if .terraform.lock.hcl is empty', async () => { + fs.readLocalFile.mockResolvedValueOnce('empty' as any); + + process.env.RENOVATE_X_TERRAFORM_LOCK_FILE = 'test'; + + expect( + await updateArtifacts({ + packageFileName: 'main.tf', + updatedDeps: ['aws'], + newPackageFileContent: '', + config, + }) + ).toBeNull(); + }); + + it('update single dependency with exact constraint', async () => { + fs.readLocalFile.mockResolvedValueOnce(validLockfile as any); + + mockHash.mockResolvedValueOnce([ + 'h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=', + 'h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=', + ]); + + const localConfig: UpdateArtifactsConfig = { + updateType: 'minor', + newVersion: '3.36.0', + newValue: '3.36.0', + ...config, + }; + + process.env.RENOVATE_X_TERRAFORM_LOCK_FILE = 'test'; + + const result = await updateArtifacts({ + packageFileName: 'main.tf', + updatedDeps: ['hashicorp/aws'], + newPackageFileContent: '', + config: localConfig, + }); + expect(result).not.toBeNull(); + expect(result).toBeArrayOfSize(1); + expect(result[0].file).not.toBeNull(); + expect(result[0].file).toMatchSnapshot(); + + expect(mockHash.mock.calls).toBeArrayOfSize(1); + expect(mockHash.mock.calls).toMatchSnapshot(); + }); + + it('update single dependency with range constraint and minor update', async () => { + fs.readLocalFile.mockResolvedValueOnce(validLockfile as any); + + mockHash.mockResolvedValueOnce([ + 'h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=', + 'h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=', + ]); + + const localConfig: UpdateArtifactsConfig = { + updateType: 'minor', + newVersion: '2.56.0', + newValue: '~> 2.50', + ...config, + }; + + process.env.RENOVATE_X_TERRAFORM_LOCK_FILE = 'test'; + + const result = await updateArtifacts({ + packageFileName: 'main.tf', + updatedDeps: ['azurerm'], + newPackageFileContent: '', + config: localConfig, + }); + expect(result).not.toBeNull(); + expect(result).toBeArrayOfSize(1); + expect(result[0].file).not.toBeNull(); + expect(result[0].file).toMatchSnapshot(); + + expect(mockHash.mock.calls).toBeArrayOfSize(1); + expect(mockHash.mock.calls).toMatchSnapshot(); + }); + + it('update single dependency with range constraint and major update', async () => { + fs.readLocalFile.mockResolvedValueOnce(validLockfile as any); + + mockHash.mockResolvedValueOnce([ + 'h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=', + 'h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=', + ]); + + const localConfig: UpdateArtifactsConfig = { + updateType: 'major', + newVersion: '3.1.0', + newValue: '~> 3.0', + ...config, + }; + + process.env.RENOVATE_X_TERRAFORM_LOCK_FILE = 'test'; + + const result = await updateArtifacts({ + packageFileName: 'main.tf', + updatedDeps: ['random'], + newPackageFileContent: '', + config: localConfig, + }); + expect(result).not.toBeNull(); + expect(result).toBeArrayOfSize(1); + expect(result[0].file).not.toBeNull(); + expect(result[0].file).toMatchSnapshot(); + + expect(mockHash.mock.calls).toBeArrayOfSize(1); + expect(mockHash.mock.calls).toMatchSnapshot(); + }); + + it('do full lock file maintenance', async () => { + fs.readLocalFile.mockResolvedValueOnce(validLockfile as any); + + mockGetPkgReleases + .mockResolvedValueOnce({ + // aws + releases: [ + { + version: '2.30.0', + }, + { + version: '3.0.0', + }, + { + version: '3.36.0', + }, + ], + }) + .mockResolvedValueOnce({ + // azurerm + releases: [ + { + version: '2.50.0', + }, + { + version: '2.55.0', + }, + { + version: '2.56.0', + }, + ], + }) + .mockResolvedValueOnce({ + // random + releases: [ + { + version: '2.2.1', + }, + { + version: '2.2.2', + }, + { + version: '3.0.0', + }, + ], + }); + mockHash.mockResolvedValue([ + 'h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=', + 'h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=', + ]); + + const localConfig: UpdateArtifactsConfig = { + updateType: 'lockFileMaintenance', + ...config, + }; + + process.env.RENOVATE_X_TERRAFORM_LOCK_FILE = 'test'; + + const result = await updateArtifacts({ + packageFileName: '', + updatedDeps: [], + newPackageFileContent: '', + config: localConfig, + }); + expect(result).not.toBeNull(); + expect(result).toBeArrayOfSize(1); + + result.forEach((value) => expect(value.file).not.toBeNull()); + result.forEach((value) => expect(value.file).toMatchSnapshot()); + + expect(mockHash.mock.calls).toBeArrayOfSize(2); + expect(mockHash.mock.calls).toMatchSnapshot(); + }); + + it('do full lock file maintenance without necessary changes', async () => { + fs.readLocalFile.mockResolvedValueOnce(validLockfile as any); + + mockGetPkgReleases + .mockResolvedValueOnce({ + // aws + releases: [ + { + version: '2.30.0', + }, + { + version: '3.0.0', + }, + ], + }) + .mockResolvedValueOnce({ + // azurerm + releases: [ + { + version: '2.50.0', + }, + ], + }) + .mockResolvedValueOnce({ + // random + releases: [ + { + version: '2.2.1', + }, + ], + }); + mockHash.mockResolvedValue([ + 'h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=', + 'h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=', + ]); + + const localConfig: UpdateArtifactsConfig = { + updateType: 'lockFileMaintenance', + ...config, + }; + const result = await updateArtifacts({ + packageFileName: '', + updatedDeps: [], + newPackageFileContent: '', + config: localConfig, + }); + expect(result).toBeNull(); + + expect(mockHash.mock.calls).toBeArrayOfSize(0); + expect(mockHash.mock.calls).toMatchSnapshot(); + }); + + it('return null if hashing fails', async () => { + fs.readLocalFile.mockResolvedValueOnce(validLockfile as any); + + mockGetPkgReleases + .mockResolvedValueOnce({ + // aws + releases: [ + { + version: '2.30.0', + }, + { + version: '3.0.0', + }, + { + version: '3.36.0', + }, + ], + }) + .mockResolvedValueOnce({ + // azurerm + releases: [ + { + version: '2.50.0', + }, + { + version: '2.55.0', + }, + { + version: '2.56.0', + }, + ], + }) + .mockResolvedValueOnce({ + // random + releases: [ + { + version: '2.2.1', + }, + { + version: '2.2.2', + }, + { + version: '3.0.0', + }, + ], + }); + mockHash.mockResolvedValue(null); + + const localConfig: UpdateArtifactsConfig = { + updateType: 'lockFileMaintenance', + ...config, + }; + + process.env.RENOVATE_X_TERRAFORM_LOCK_FILE = 'test'; + + const result = await updateArtifacts({ + packageFileName: '', + updatedDeps: [], + newPackageFileContent: '', + config: localConfig, + }); + expect(result).toBeNull(); + + expect(mockHash.mock.calls).toBeArrayOfSize(2); + expect(mockHash.mock.calls).toMatchSnapshot(); + }); + + it('return null if experimental flag is not set', async () => { + const localConfig: UpdateArtifactsConfig = { + updateType: 'lockFileMaintenance', + ...config, + }; + const result = await updateArtifacts({ + packageFileName: '', + updatedDeps: [], + newPackageFileContent: '', + config: localConfig, + }); + expect(result).toBeNull(); + }); +}); diff --git a/lib/manager/terraform/lockfile/index.ts b/lib/manager/terraform/lockfile/index.ts new file mode 100644 index 00000000000000..2fb8b5a6e8b877 --- /dev/null +++ b/lib/manager/terraform/lockfile/index.ts @@ -0,0 +1,112 @@ +import pMap from 'p-map'; +import { GetPkgReleasesConfig, getPkgReleases } from '../../../datasource'; +import { logger } from '../../../logger'; +import { get as getVersioning } from '../../../versioning'; +import type { UpdateArtifact, UpdateArtifactsResult } from '../../types'; +import { createHashes } from './hash'; +import type { ProviderLock, ProviderLockUpdate } from './types'; +import { + extractLocks, + isPinnedVersion, + readLockFile, + writeLockUpdates, +} from './util'; + +async function updateAllLocks( + locks: ProviderLock[] +): Promise { + const updates = await pMap( + locks, + async (lock) => { + const updateConfig: GetPkgReleasesConfig = { + versioning: 'hashicorp', + datasource: 'terraform-provider', + depName: lock.lookupName, + }; + const { releases } = await getPkgReleases(updateConfig); + const versioning = getVersioning(updateConfig.versioning); + const versionsList = releases.map((release) => release.version); + const newVersion = versioning.getSatisfyingVersion( + versionsList, + lock.constraints + ); + + // if the new version is the same as the last, signal that no update is needed + if (newVersion === lock.version) { + return null; + } + const update: ProviderLockUpdate = { + newVersion, + newConstraint: lock.constraints, + newHashes: await createHashes(lock.lookupName, newVersion), + ...lock, + }; + return update; + }, + { concurrency: 4 } // allow to look up 4 lock in parallel + ); + + return updates.filter(Boolean); +} + +export async function updateArtifacts({ + packageFileName, + updatedDeps, + config, +}: UpdateArtifact): Promise { + logger.debug(`terraform.updateArtifacts(${packageFileName})`); + + // TODO remove experimental flag, if functionality is confirmed + if (!process.env.RENOVATE_X_TERRAFORM_LOCK_FILE) { + logger.debug( + `terraform.updateArtifacts: skipping updates. Experimental feature not activated` + ); + return null; + } + + const lockFileContent = await readLockFile(packageFileName); + if (!lockFileContent) { + logger.debug('No .terraform.lock.hcl found'); + return null; + } + const locks = extractLocks(lockFileContent); + if (!locks) { + logger.debug('No Locks in .terraform.lock.hcl found'); + return null; + } + + const updates: ProviderLockUpdate[] = []; + if (config.updateType === 'lockFileMaintenance') { + // update all locks in the file during maintenance --> only update version in constraints + const maintenanceUpdates = await updateAllLocks(locks); + updates.push(...maintenanceUpdates); + } else { + // update only specific locks but with constrain updates + const lookupName = updatedDeps[0]; + const repository = lookupName.includes('/') + ? lookupName + : `hashicorp/${lookupName}`; + const newConstraint = isPinnedVersion(config.newValue) + ? config.newVersion + : config.newValue; + const updateLock = locks.find((value) => value.lookupName === repository); + const update: ProviderLockUpdate = { + newVersion: config.newVersion, + newConstraint, + newHashes: await createHashes(repository, config.newVersion), + ...updateLock, + }; + updates.push(update); + } + + // if no updates have been found or there are failed hashes abort + if ( + updates.length === 0 || + updates.some((value) => value.newHashes == null) + ) { + return null; + } + + const res = writeLockUpdates(updates, lockFileContent); + return res ? [res] : null; +} diff --git a/lib/manager/terraform/lockfile/types.ts b/lib/manager/terraform/lockfile/types.ts new file mode 100644 index 00000000000000..b5cb56299c7183 --- /dev/null +++ b/lib/manager/terraform/lockfile/types.ts @@ -0,0 +1,35 @@ +export interface ProviderLock { + lookupName: string; + registryUrl: string; + version: string; + constraints: string; + hashes: string[]; + lineNumbers: LineNumbers; +} + +export interface ProviderLockUpdate extends ProviderLock { + newVersion: string; + newConstraint: string; + newHashes: string[]; +} + +export interface ProviderSlice { + lines: string[]; + block: { + start: number; + end: number; + }; +} + +export interface LineNumbers { + version?: number; + constraint?: number; + block?: { + start: number; + end: number; + }; + hashes: { + start?: number; + end?: number; + }; +} diff --git a/lib/manager/terraform/lockfile/util.spec.ts b/lib/manager/terraform/lockfile/util.spec.ts new file mode 100644 index 00000000000000..77e60d7f19d8bb --- /dev/null +++ b/lib/manager/terraform/lockfile/util.spec.ts @@ -0,0 +1,19 @@ +import { getName, loadFixture } from '../../../../test/util'; +import { extractLocks } from './util'; + +const validLockfile = loadFixture('validLockfile.hcl'); + +describe(getName(), () => { + describe('extractLocks()', () => { + it('returns null for empty', () => { + const result = extractLocks('nothing here'); + expect(result).toBeNull(); + }); + + it('extracts', () => { + const res = extractLocks(validLockfile); + expect(res).toHaveLength(3); + expect(res).toMatchSnapshot(); + }); + }); +}); diff --git a/lib/manager/terraform/lockfile/util.ts b/lib/manager/terraform/lockfile/util.ts new file mode 100644 index 00000000000000..ae2bfcbcbdeb7d --- /dev/null +++ b/lib/manager/terraform/lockfile/util.ts @@ -0,0 +1,219 @@ +import { join } from 'upath'; +import { getAdminConfig } from '../../../config/admin'; +import { logger } from '../../../logger'; +import { ensureDir, getSiblingFileName, readLocalFile } from '../../../util/fs'; +import { get as getVersioning } from '../../../versioning'; +import type { UpdateArtifactsResult } from '../../types'; +import type { + LineNumbers, + ProviderLock, + ProviderLockUpdate, + ProviderSlice, +} from './types'; + +export const repositoryRegex = /^hashicorp\/(?\S+)$/; + +const providerStartLineRegex = + /^provider "(?[^/]*)\/(?[^/]*)\/(?[^/]*)"/; +const versionLineRegex = + /^(?[\s]*version[\s]*=[\s]*")(?[^"']+)(?".*)$/; +const constraintLineRegex = + /^(?[\s]*constraints[\s]*=[\s]*")(?[^"']+)(?".*)$/; +const hashLineRegex = /^(?\s*")(?[^"]+)(?",.*)$/; + +const lockFile = '.terraform.lock.hcl'; + +export function readLockFile(packageFilePath: string): Promise { + const lockFilePath = getSiblingFileName(packageFilePath, lockFile); + return readLocalFile(lockFilePath, 'utf8'); +} + +export function extractLocks(lockFileContent: string): ProviderLock[] { + const lines = lockFileContent.split('\n'); + const blockStarts: number[] = []; + // get first lines of blocks + lines.forEach((line, index) => { + if (line.startsWith('provider "')) { + blockStarts.push(index); + } + }); + + // sort ascending + const sortedStarts = blockStarts.sort((a, b) => a - b); + const contentSlices = sortedStarts.map((start, index, array) => { + let end: number; + if (index < array.length - 1) { + end = array[index + 1]; + } else { + end = lines.length - 1; + } + const slice: ProviderSlice = { + lines: lines.slice(start, end), + block: { + start, + end, + }, + }; + return slice; + }); + + // generate Lock objects from slices + const locks = contentSlices.map((slice) => { + let lookupName = ''; + let registryUrl = ''; + let version = ''; + let constraints = ''; + const relativeLineNumbers: LineNumbers = { + block: slice.block, + hashes: { + start: -1, + end: -1, + }, + }; + const hashes = []; + + slice.lines.forEach((line, index) => { + const hashLineResult = hashLineRegex.exec(line); + if (hashLineResult) { + hashes.push(hashLineResult.groups.hash); + relativeLineNumbers.hashes.start = + relativeLineNumbers.hashes.start === -1 + ? index + : relativeLineNumbers.hashes.start; + relativeLineNumbers.hashes.end = index; + return; + } + + const providerStartLineResult = providerStartLineRegex.exec(line); + if (providerStartLineResult) { + lookupName = `${providerStartLineResult.groups.namespace}/${providerStartLineResult.groups.depName}`; + registryUrl = providerStartLineResult.groups.registryUrl; + return; + } + + const versionLineResult = versionLineRegex.exec(line); + if (versionLineResult) { + version = versionLineResult.groups.version; + relativeLineNumbers.version = index; + return; + } + + const constraintLineResult = constraintLineRegex.exec(line); + if (constraintLineResult) { + constraints = constraintLineResult.groups.constraint; + relativeLineNumbers.constraint = index; + } + }); + + const lock: ProviderLock = { + lookupName, + registryUrl, + version, + constraints, + hashes, + lineNumbers: relativeLineNumbers, + }; + return lock; + }); + + if (locks.length === 0) { + return null; + } + return locks; +} + +export function isPinnedVersion(value: string): boolean { + const versioning = getVersioning('hashicorp'); + return !!versioning.isSingleVersion(value); +} + +export function writeLockUpdates( + updates: ProviderLockUpdate[], + oldLockFileContent: string +): UpdateArtifactsResult { + const lines = oldLockFileContent.split('\n'); + + const sections: string[][] = []; + updates.forEach((update, index, array) => { + // re add leading whitespace + let startWhitespace; + if (index > 0) { + // get end of the + startWhitespace = array[index - 1].lineNumbers.block.end; + } + const leadingNonRelevantLines = lines.slice( + startWhitespace, + update.lineNumbers.block.start + ); + sections.push(leadingNonRelevantLines); + + const providerBlockLines = lines.slice( + update.lineNumbers.block.start, + update.lineNumbers.block.end + ); + const newProviderBlockLines: string[] = []; + let hashLinePrefix = ''; + let hashLineSuffix = ''; + providerBlockLines.forEach((providerBlockLine, providerBlockIndex) => { + const versionLine = providerBlockLine.replace( + versionLineRegex, + `$1${update.newVersion}$3` + ); + if (versionLine !== providerBlockLine) { + newProviderBlockLines.push(versionLine); + return; + } + + const constraintLine = providerBlockLine.replace( + constraintLineRegex, + `$1${update.newConstraint}$3` + ); + if (constraintLine !== providerBlockLine) { + newProviderBlockLines.push(constraintLine); + return; + } + + const hashLineRegexResult = hashLineRegex.exec(providerBlockLine); + if (hashLineRegexResult) { + // skip hash line but safe the whitespace + hashLinePrefix = hashLineRegexResult.groups.prefix; + hashLineSuffix = hashLineRegexResult.groups.suffix; + return; + } + newProviderBlockLines.push(providerBlockLine); + }); + const hashesWithWhitespace = update.newHashes.map( + (value) => `${hashLinePrefix}${value}${hashLineSuffix}` + ); + newProviderBlockLines.splice( + update.lineNumbers.hashes.start, + 0, + ...hashesWithWhitespace + ); + sections.push(newProviderBlockLines); + }); + + const trailingNotUpdatedLines = lines.slice( + updates[updates.length - 1].lineNumbers.block.end + ); + sections.push(trailingNotUpdatedLines); + + const newLines = sections.reduce((previousValue, currentValue) => + previousValue.concat(currentValue) + ); + const newContent = newLines.join('\n'); + + return { + file: { + name: lockFile, + contents: newContent, + }, + }; +} + +export async function getCacheDir(): Promise { + const cacheDir = join(getAdminConfig().cacheDir, './others/terraform'); + await ensureDir(cacheDir); + logger.debug(`Using terraform cache: ${cacheDir}`); + return cacheDir; +} diff --git a/lib/manager/terraform/providers.ts b/lib/manager/terraform/providers.ts index ffef811c086cdc..6feb0f9209c1ce 100644 --- a/lib/manager/terraform/providers.ts +++ b/lib/manager/terraform/providers.ts @@ -1,5 +1,5 @@ import is from '@sindresorhus/is'; -import * as datasourceTerraformProvider from '../../datasource/terraform-provider'; +import { TerraformProviderDatasource } from '../../datasource/terraform-provider'; import { logger } from '../../logger'; import { SkipReason } from '../../types'; import type { PackageDependency } from '../types'; @@ -62,7 +62,7 @@ export function analyzeTerraformProvider(dep: PackageDependency): void { /* eslint-disable no-param-reassign */ dep.depType = 'provider'; dep.depName = dep.managerData.moduleName; - dep.datasource = datasourceTerraformProvider.id; + dep.datasource = TerraformProviderDatasource.id; if (is.nonEmptyString(dep.managerData.source)) { const source = sourceExtractionRegex.exec(dep.managerData.source); diff --git a/lib/manager/terraform/resources.ts b/lib/manager/terraform/resources.ts index 7cf7799639a967..075a1b63b34589 100644 --- a/lib/manager/terraform/resources.ts +++ b/lib/manager/terraform/resources.ts @@ -1,4 +1,4 @@ -import * as datasourceHelm from '../../datasource/helm'; +import { HelmDatasource } from '../../datasource/helm'; import { SkipReason } from '../../types'; import { getDep } from '../dockerfile/extract'; import type { PackageDependency } from '../types'; @@ -104,7 +104,7 @@ export function analyseTerraformResource( dep.depType = 'helm_release'; dep.registryUrls = [dep.managerData.repository]; dep.depName = dep.managerData.chart; - dep.datasource = datasourceHelm.id; + dep.datasource = HelmDatasource.id; break; default: diff --git a/lib/manager/types.ts b/lib/manager/types.ts index 40b7f21c2b953b..79c1da65629db4 100644 --- a/lib/manager/types.ts +++ b/lib/manager/types.ts @@ -10,7 +10,6 @@ import type { File } from '../util/git'; export type Result = T | Promise; export interface ManagerConfig { - binarySource?: string; registryUrls?: string[]; } @@ -19,7 +18,6 @@ export interface ManagerData { } export interface ExtractConfig { - binarySource?: string; registryUrls?: string[]; endpoint?: string; gradle?: { timeout?: number }; @@ -145,7 +143,7 @@ export interface LookupUpdate { newMinor?: number; newValue: string; semanticCommitType?: string; - pendingChecks?: string[]; + pendingChecks?: boolean; pendingVersions?: string[]; newVersion?: string; updateType?: UpdateType; diff --git a/lib/platform/bitbucket-server/index.spec.ts b/lib/platform/bitbucket-server/index.spec.ts index 45d6d558ff97fb..cb747f2e034ac3 100644 --- a/lib/platform/bitbucket-server/index.spec.ts +++ b/lib/platform/bitbucket-server/index.spec.ts @@ -1,4 +1,3 @@ -import nock from 'nock'; import * as httpMock from '../../../test/http-mock'; import { getName } from '../../../test/util'; import { @@ -181,7 +180,7 @@ describe(getName(), () => { const username = 'abc'; const password = '123'; - async function initRepo(config = {}): Promise { + async function initRepo(config = {}): Promise { const scope = httpMock .scope(urlHost) .get(`${urlPath}/rest/api/1.0/projects/SOME/repos/repo`) @@ -203,8 +202,6 @@ describe(getName(), () => { beforeEach(async () => { // reset module jest.resetModules(); - httpMock.reset(); - httpMock.setup(); jest.mock('delay'); jest.mock('../../util/git'); jest.mock('../../util/host-rules'); @@ -230,9 +227,6 @@ describe(getName(), () => { password, }); }); - afterEach(() => { - httpMock.reset(); - }); describe('initPlatform()', () => { it('should throw if no endpoint', () => { diff --git a/lib/platform/bitbucket-server/index.ts b/lib/platform/bitbucket-server/index.ts index 4f0a81e3d53390..57c6d5525e8eed 100644 --- a/lib/platform/bitbucket-server/index.ts +++ b/lib/platform/bitbucket-server/index.ts @@ -990,8 +990,6 @@ export async function mergePr( } logger.debug({ pr: prNo }, 'PR merged'); - // Delete branch - await deleteBranch(branchName); return true; } diff --git a/lib/platform/bitbucket/comments.spec.ts b/lib/platform/bitbucket/comments.spec.ts index 2556bdbd84feb7..aebc255bb7f502 100644 --- a/lib/platform/bitbucket/comments.spec.ts +++ b/lib/platform/bitbucket/comments.spec.ts @@ -11,14 +11,8 @@ describe(getName(), () => { beforeEach(() => { jest.clearAllMocks(); - httpMock.reset(); - httpMock.setup(); - setBaseUrl(baseUrl); }); - afterEach(() => { - httpMock.reset(); - }); describe('ensureComment()', () => { it('does not throw', async () => { diff --git a/lib/platform/bitbucket/index.spec.ts b/lib/platform/bitbucket/index.spec.ts index d0247622756d68..ece1e2fb5cc2c5 100644 --- a/lib/platform/bitbucket/index.spec.ts +++ b/lib/platform/bitbucket/index.spec.ts @@ -1,4 +1,3 @@ -import nock from 'nock'; import * as httpMock from '../../../test/http-mock'; import { getName } from '../../../test/util'; import { logger as _logger } from '../../logger'; @@ -43,8 +42,6 @@ describe(getName(), () => { beforeEach(async () => { // reset module jest.resetModules(); - httpMock.reset(); - httpMock.setup(); jest.mock('../../util/git'); jest.mock('../../util/host-rules'); jest.mock('../../logger'); @@ -63,15 +60,12 @@ describe(getName(), () => { setBaseUrl(baseUrl); }); - afterEach(() => { - httpMock.reset(); - }); async function initRepoMock( config?: Partial, repoResp?: any, - existingScope?: nock.Scope - ): Promise { + existingScope?: httpMock.Scope + ): Promise { const repository = config?.repository || 'some/repo'; const scope = existingScope || httpMock.scope(baseUrl); diff --git a/lib/platform/bitbucket/utils.spec.ts b/lib/platform/bitbucket/utils.spec.ts index ca2c34ea35e3ec..39da0c4061a018 100644 --- a/lib/platform/bitbucket/utils.spec.ts +++ b/lib/platform/bitbucket/utils.spec.ts @@ -8,14 +8,9 @@ const baseUrl = 'https://api.bitbucket.org'; describe('accumulateValues()', () => { beforeEach(() => { - httpMock.setup(); setBaseUrl(baseUrl); }); - afterEach(() => { - httpMock.reset(); - }); - it('paginates', async () => { httpMock .scope(baseUrl) diff --git a/lib/platform/gitea/__snapshots__/gitea-helper.spec.ts.snap b/lib/platform/gitea/__snapshots__/gitea-helper.spec.ts.snap index 9d40fd5b7d4d7d..500591aef74753 100644 --- a/lib/platform/gitea/__snapshots__/gitea-helper.spec.ts.snap +++ b/lib/platform/gitea/__snapshots__/gitea-helper.spec.ts.snap @@ -456,6 +456,21 @@ Array [ ] `; +exports[`platform/gitea/gitea-helper getIssue should call /api/v1/repos/[repo]/issues/[issue] endpoint 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "host": "gitea.renovatebot.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://gitea.renovatebot.com/api/v1/repos/some/repo/issues/7", + }, +] +`; + exports[`platform/gitea/gitea-helper getOrgLabels should call /api/v1/orgs/[org]/labels endpoint 1`] = ` Array [ Object { diff --git a/lib/platform/gitea/gitea-helper.spec.ts b/lib/platform/gitea/gitea-helper.spec.ts index 2213effdb80221..4095b539dc8004 100644 --- a/lib/platform/gitea/gitea-helper.spec.ts +++ b/lib/platform/gitea/gitea-helper.spec.ts @@ -140,13 +140,8 @@ describe(getName(), () => { beforeEach(() => { jest.resetAllMocks(); - httpMock.reset(); - httpMock.setup(); setBaseUrl(baseUrl); }); - afterEach(() => { - httpMock.reset(); - }); describe('getCurrentUser', () => { it('should call /api/v1/user endpoint', async () => { @@ -513,6 +508,19 @@ describe(getName(), () => { }); }); + describe('getIssue', () => { + it('should call /api/v1/repos/[repo]/issues/[issue] endpoint', async () => { + httpMock + .scope(baseUrl) + .get(`/repos/${mockRepo.full_name}/issues/${mockIssue.number}`) + .reply(200, mockIssue); + + const res = await ght.getIssue(mockRepo.full_name, mockIssue.number); + expect(res).toEqual(mockIssue); + expect(httpMock.getTrace()).toMatchSnapshot(); + }); + }); + describe('getRepoLabels', () => { it('should call /api/v1/repos/[repo]/labels endpoint', async () => { httpMock diff --git a/lib/platform/gitea/gitea-helper.ts b/lib/platform/gitea/gitea-helper.ts index f0c9a0b36c1283..88e2bec4065463 100644 --- a/lib/platform/gitea/gitea-helper.ts +++ b/lib/platform/gitea/gitea-helper.ts @@ -400,6 +400,16 @@ export async function searchIssues( return res.body; } +export async function getIssue( + repoPath: string, + idx: number, + options?: GiteaHttpOptions +): Promise { + const url = `repos/${repoPath}/issues/${idx}`; + const res = await giteaHttp.getJson(url, options); + return res.body; +} + export async function getRepoLabels( repoPath: string, options?: GiteaHttpOptions diff --git a/lib/platform/gitea/index.spec.ts b/lib/platform/gitea/index.spec.ts index 8d42f712ec1ba8..d5ab8c01d8dcd1 100644 --- a/lib/platform/gitea/index.spec.ts +++ b/lib/platform/gitea/index.spec.ts @@ -905,10 +905,24 @@ describe(getName(), () => { }); }); + describe('getIssue', () => { + it('should return the issue', async () => { + const mockIssue = mockIssues.find((i) => i.number === 1); + helper.getIssue.mockResolvedValueOnce(mockIssue); + await initFakeRepo(); + + expect(await gitea.getIssue(mockIssue.number)).toHaveProperty( + 'number', + mockIssue.number + ); + }); + }); + describe('findIssue', () => { it('should return existing open issue', async () => { const mockIssue = mockIssues.find((i) => i.title === 'open-issue'); helper.searchIssues.mockResolvedValueOnce(mockIssues); + helper.getIssue.mockResolvedValueOnce(mockIssue); await initFakeRepo(); expect(await gitea.findIssue(mockIssue.title)).toHaveProperty( diff --git a/lib/platform/gitea/index.ts b/lib/platform/gitea/index.ts index a09a65b261d490..0458ff5758f7fe 100644 --- a/lib/platform/gitea/index.ts +++ b/lib/platform/gitea/index.ts @@ -587,16 +587,34 @@ const platform: Platform = { return config.issueList; }, + async getIssue(number: number, useCache = true): Promise { + try { + const body = ( + await helper.getIssue(config.repository, number, { + useCache, + }) + ).body; + return { + number, + body, + }; + } catch (err) /* istanbul ignore next */ { + logger.debug({ err, number }, 'Error getting issue'); + return null; + } + }, + async findIssue(title: string): Promise { const issueList = await platform.getIssueList(); const issue = issueList.find( (i) => i.state === 'open' && i.title === title ); - if (issue) { - logger.debug(`Found Issue #${issue.number}`); + if (!issue) { + return null; } - return issue ?? null; + logger.debug(`Found Issue #${issue.number}`); + return platform.getIssue(issue.number); }, async ensureIssue({ @@ -836,6 +854,7 @@ export const { getBranchPr, getBranchStatus, getBranchStatusCheck, + getIssue, getRawFile, getJsonFile, getIssueList, diff --git a/lib/platform/github/index.spec.ts b/lib/platform/github/index.spec.ts index aa84436489261e..ed7d87995ea995 100644 --- a/lib/platform/github/index.spec.ts +++ b/lib/platform/github/index.spec.ts @@ -34,11 +34,6 @@ describe(getName(), () => { hostRules.find.mockReturnValue({ token: 'abc123', }); - httpMock.setup(); - }); - - afterEach(() => { - httpMock.reset(); }); const graphqlOpenPullRequests = loadFixture('graphql/pullrequest-1.json'); diff --git a/lib/platform/github/index.ts b/lib/platform/github/index.ts index 18a7ca783828e2..0363732eee9cc6 100644 --- a/lib/platform/github/index.ts +++ b/lib/platform/github/index.ts @@ -20,7 +20,6 @@ import { logger } from '../../logger'; import { BranchStatus, PrState, VulnerabilityAlert } from '../../types'; import { ExternalHostError } from '../../types/errors/external-host-error'; import * as git from '../../util/git'; -import { deleteBranch } from '../../util/git'; import * as hostRules from '../../util/host-rules'; import * as githubHttp from '../../util/http/github'; import { sanitize } from '../../util/sanitize'; @@ -1106,6 +1105,27 @@ export async function getIssueList(): Promise { return config.issueList; } +export async function getIssue( + number: number, + useCache = true +): Promise { + try { + const issueBody = ( + await githubApi.getJson<{ body: string }>( + `repos/${config.parentRepo || config.repository}/issues/${number}`, + { useCache } + ) + ).body.body; + return { + number, + body: issueBody, + }; + } catch (err) /* istanbul ignore next */ { + logger.debug({ err, number }, 'Error getting issue'); + return null; + } +} + export async function findIssue(title: string): Promise { logger.debug(`findIssue(${title})`); const [issue] = (await getIssueList()).filter( @@ -1115,15 +1135,7 @@ export async function findIssue(title: string): Promise { return null; } logger.debug(`Found issue ${issue.number}`); - const issueBody = ( - await githubApi.getJson<{ body: string }>( - `repos/${config.parentRepo || config.repository}/issues/${issue.number}` - ) - ).body.body; - return { - number: issue.number, - body: issueBody, - }; + return getIssue(issue.number); } async function closeIssue(issueNumber: number): Promise { @@ -1584,12 +1596,13 @@ export async function mergePr( options.token = config.forkToken; } let automerged = false; + let automergeResult: any; if (config.mergeMethod) { // This path is taken if we have auto-detected the allowed merge types from the repo options.body.merge_method = config.mergeMethod; try { logger.debug({ options, url }, `mergePr`); - await githubApi.putJson(url, options); + automergeResult = await githubApi.putJson(url, options); automerged = true; } catch (err) { if (err.statusCode === 404 || err.statusCode === 405) { @@ -1609,19 +1622,19 @@ export async function mergePr( options.body.merge_method = 'rebase'; try { logger.debug({ options, url }, `mergePr`); - await githubApi.putJson(url, options); + automergeResult = await githubApi.putJson(url, options); } catch (err1) { logger.debug({ err: err1 }, `Failed to rebase merge PR`); try { options.body.merge_method = 'squash'; logger.debug({ options, url }, `mergePr`); - await githubApi.putJson(url, options); + automergeResult = await githubApi.putJson(url, options); } catch (err2) { logger.debug({ err: err2 }, `Failed to merge squash PR`); try { options.body.merge_method = 'merge'; logger.debug({ options, url }, `mergePr`); - await githubApi.putJson(url, options); + automergeResult = await githubApi.putJson(url, options); } catch (err3) { logger.debug({ err: err3 }, `Failed to merge commit PR`); logger.info({ pr: prNo }, 'All merge attempts failed'); @@ -1630,9 +1643,10 @@ export async function mergePr( } } } - logger.debug({ pr: prNo }, 'PR merged'); - // Delete branch - await deleteBranch(branchName); + logger.debug( + { automergeResult: automergeResult.body, pr: prNo }, + 'PR merged' + ); return true; } diff --git a/lib/platform/gitlab/__snapshots__/index.spec.ts.snap b/lib/platform/gitlab/__snapshots__/index.spec.ts.snap index f63b6d3f9fa792..241719abb8ef1b 100644 --- a/lib/platform/gitlab/__snapshots__/index.spec.ts.snap +++ b/lib/platform/gitlab/__snapshots__/index.spec.ts.snap @@ -2039,6 +2039,61 @@ Array [ ] `; +exports[`platform/gitlab/index initRepo should fall back respecting when GITLAB_IGNORE_REPO_URL is set 1`] = ` +Array [ + Array [ + Object { + "cloneSubmodules": undefined, + "defaultBranch": "master", + "gitAuthorEmail": undefined, + "gitAuthorName": undefined, + "ignorePrAuthor": undefined, + "mergeMethod": "merge", + "repository": "some%2Frepo%2Fproject", + "url": "http://oauth2:abc123@mycompany.com/gitlab/some/repo/project.git", + }, + ], +] +`; + +exports[`platform/gitlab/index initRepo should fall back respecting when GITLAB_IGNORE_REPO_URL is set 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer mytoken", + "host": "mycompany.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://mycompany.com/gitlab/api/v4/user", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer mytoken", + "host": "mycompany.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://mycompany.com/gitlab/api/v4/version", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer abc123", + "host": "mycompany.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://mycompany.com/gitlab/api/v4/projects/some%2Frepo%2Fproject", + }, +] +`; + exports[`platform/gitlab/index initRepo should throw an error if MRs are disabled 1`] = ` Array [ Object { diff --git a/lib/platform/gitlab/index.spec.ts b/lib/platform/gitlab/index.spec.ts index a436586ac9898b..76a0a00e125017 100644 --- a/lib/platform/gitlab/index.spec.ts +++ b/lib/platform/gitlab/index.spec.ts @@ -1,5 +1,4 @@ // TODO fix mocks -import nock from 'nock'; import { Platform, RepoParams } from '..'; import * as httpMock from '../../../test/http-mock'; import { getName } from '../../../test/util'; @@ -43,11 +42,7 @@ describe(getName(), () => { hostRules.find.mockReturnValue({ token: 'abc123', }); - httpMock.reset(); - httpMock.setup(); - }); - afterEach(() => { - httpMock.reset(); + delete process.env.GITLAB_IGNORE_REPO_URL; }); async function initFakePlatform(version: string) { @@ -167,7 +162,7 @@ describe(getName(), () => { }, repoResp = null, scope = httpMock.scope(gitlabApiHost) - ): Promise { + ): Promise { const repo = repoParams.repository; const justRepo = repo.split('/').slice(0, 2).join('/'); scope.get(`/api/v4/projects/${encodeURIComponent(repo)}`).reply( @@ -290,6 +285,38 @@ describe(getName(), () => { }); expect(httpMock.getTrace()).toMatchSnapshot(); }); + + it('should fall back respecting when GITLAB_IGNORE_REPO_URL is set', async () => { + process.env.GITLAB_IGNORE_REPO_URL = 'true'; + const selfHostedUrl = 'http://mycompany.com/gitlab'; + httpMock + .scope(selfHostedUrl) + .get('/api/v4/user') + .reply(200, { + email: 'a@b.com', + name: 'Renovate Bot', + }) + .get('/api/v4/version') + .reply(200, { + version: '13.8.0', + }); + await gitlab.initPlatform({ + endpoint: `${selfHostedUrl}/api/v4`, + token: 'mytoken', + }); + httpMock + .scope(selfHostedUrl) + .get('/api/v4/projects/some%2Frepo%2Fproject') + .reply(200, { + default_branch: 'master', + http_url_to_repo: `http://other.host.com/gitlab/some/repo/project.git`, + }); + await gitlab.initRepo({ + repository: 'some/repo/project', + }); + expect(git.initRepo.mock.calls).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); + }); }); describe('getRepoForceRebase', () => { it('should return false', async () => { diff --git a/lib/platform/gitlab/index.ts b/lib/platform/gitlab/index.ts index 87f86415f444f4..da190b10893409 100755 --- a/lib/platform/gitlab/index.ts +++ b/lib/platform/gitlab/index.ts @@ -22,7 +22,7 @@ import * as hostRules from '../../util/host-rules'; import { HttpResponse } from '../../util/http'; import { setBaseUrl } from '../../util/http/gitlab'; import { sanitize } from '../../util/sanitize'; -import { ensureTrailingSlash, getQueryString } from '../../util/url'; +import { ensureTrailingSlash, getQueryString, parseUrl } from '../../util/url'; import type { BranchStatusConfig, CreatePRConfig, @@ -225,13 +225,15 @@ export async function initRepo({ res.body.http_url_to_repo === null ) { logger.debug('no http_url_to_repo found. Falling back to old behaviour.'); - const { host, protocol } = URL.parse(defaults.endpoint); - url = git.getUrl({ - protocol: protocol.slice(0, -1) as any, + const { protocol, host, pathname } = parseUrl(defaults.endpoint); + const newPathname = pathname.slice(0, pathname.indexOf('/api')); + url = URL.format({ + protocol: protocol.slice(0, -1) || 'https', auth: 'oauth2:' + opts.token, host, - repository, + pathname: newPathname + '/' + repository + '.git', }); + logger.debug({ url }, 'using URL based on configured endpoint'); } else { logger.debug(`${repository} http URL = ${res.body.http_url_to_repo}`); const repoUrl = URL.parse(`${res.body.http_url_to_repo}`); @@ -751,23 +753,36 @@ export async function getIssueList(): Promise { return config.issueList; } -export async function findIssue(title: string): Promise { - logger.debug(`findIssue(${title})`); +export async function getIssue( + number: number, + useCache = true +): Promise { try { - const issueList = await getIssueList(); - const issue = issueList.find((i) => i.title === title); - if (!issue) { - return null; - } const issueBody = ( await gitlabApi.getJson<{ description: string }>( - `projects/${config.repository}/issues/${issue.iid}` + `projects/${config.repository}/issues/${number}`, + { useCache } ) ).body.description; return { - number: issue.iid, + number, body: issueBody, }; + } catch (err) /* istanbul ignore next */ { + logger.debug({ err, number }, 'Error getting issue'); + return null; + } +} + +export async function findIssue(title: string): Promise { + logger.debug(`findIssue(${title})`); + try { + const issueList = await getIssueList(); + const issue = issueList.find((i) => i.title === title); + if (!issue) { + return null; + } + return getIssue(issue.iid); } catch (err) /* istanbul ignore next */ { logger.warn('Error finding issue'); return null; diff --git a/lib/platform/index.ts b/lib/platform/index.ts index 0117466a545fc1..8268aecdc7c1da 100644 --- a/lib/platform/index.ts +++ b/lib/platform/index.ts @@ -1,6 +1,6 @@ import URL from 'url'; import addrs from 'email-addresses'; -import type { GlobalConfig } from '../config/types'; +import type { AllConfig } from '../config/types'; import { PLATFORM_NOT_FOUND } from '../constants/error-messages'; import { logger } from '../logger'; import type { HostRule } from '../types'; @@ -81,9 +81,7 @@ export function parseGitAuthor(input: string): GitAuthor | null { return null; } -export async function initPlatform( - config: GlobalConfig -): Promise { +export async function initPlatform(config: AllConfig): Promise { setPrivateKey(config.gitPrivateKey); setNoVerify(config.gitNoVerify ?? []); setPlatformApi(config.platform); diff --git a/lib/platform/types.ts b/lib/platform/types.ts index 240425ace74b9f..dc73dda2298009 100644 --- a/lib/platform/types.ts +++ b/lib/platform/types.ts @@ -139,6 +139,7 @@ export type EnsureIssueResult = 'updated' | 'created'; export interface Platform { findIssue(title: string): Promise; getIssueList(): Promise; + getIssue?(number: number, useCache?: boolean): Promise; getVulnerabilityAlerts(): Promise; getRawFile(fileName: string, repo?: string): Promise; getJsonFile(fileName: string, repo?: string): Promise; diff --git a/lib/util/cache/package/decorator.spec.ts b/lib/util/cache/package/decorator.spec.ts new file mode 100644 index 00000000000000..d4ca78a928ee14 --- /dev/null +++ b/lib/util/cache/package/decorator.spec.ts @@ -0,0 +1,59 @@ +import os from 'os'; +import { mock } from 'jest-mock-extended'; +import { getName } from '../../../../test/util'; +import type { GetReleasesConfig } from '../../../datasource'; +import * as memCache from '../memory'; +import { cache } from './decorator'; +import * as packageCache from '.'; + +jest.mock('./file'); + +describe(getName(), () => { + const spy = jest.fn(() => Promise.resolve()); + + beforeAll(() => { + memCache.init(); + packageCache.init({ cacheDir: os.tmpdir() }); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should cache string', async () => { + class MyClass { + @cache({ namespace: 'namespace', key: 'key' }) + public async getNumber(): Promise { + await spy(); + return Math.random(); + } + } + const myClass = new MyClass(); + expect(await myClass.getNumber()).toEqual(await myClass.getNumber()); + expect(await myClass.getNumber()).not.toBeUndefined(); + expect(spy).toHaveBeenCalledTimes(1); + }); + + it('should cache function', async () => { + class MyClass { + @cache({ + namespace: (arg: GetReleasesConfig) => arg.registryUrl, + key: () => 'key', + }) + public async getNumber(_: GetReleasesConfig): Promise { + await spy(); + return Math.random(); + } + } + const myClass = new MyClass(); + const getReleasesConfig: GetReleasesConfig = { + registryUrl: 'registry', + ...mock(), + }; + expect(await myClass.getNumber(getReleasesConfig)).toEqual( + await myClass.getNumber(getReleasesConfig) + ); + expect(await myClass.getNumber(getReleasesConfig)).not.toBeUndefined(); + expect(spy).toHaveBeenCalledTimes(1); + }); +}); diff --git a/lib/util/cache/package/decorator.ts b/lib/util/cache/package/decorator.ts new file mode 100644 index 00000000000000..3de488f59c5ccc --- /dev/null +++ b/lib/util/cache/package/decorator.ts @@ -0,0 +1,120 @@ +import is from '@sindresorhus/is'; +import * as packageCache from '.'; + +type Handler = (parameters: DecoratorParameters) => Promise; +type Method = (this: T, ...args: any[]) => Promise; +type Decorator = ( + target: U, + key: keyof U, + descriptor: TypedPropertyDescriptor> +) => TypedPropertyDescriptor>; + +interface DecoratorParameters { + /** + * Current call arguments. + */ + args: U; + + /** + * A callback to call the decorated method with the current arguments. + */ + callback(): unknown; + + /** + * Current call context. + */ + instance: T; +} + +/** + * Applies decorating function to intercept decorated method calls. + * @param fn - The decorating function. + */ +function decorate(fn: Handler): Decorator { + const result: Decorator = ( + target, + key, + descriptor = Object.getOwnPropertyDescriptor(target, key) ?? { + enumerable: true, + configurable: true, + writable: true, + } + ) => { + const { value } = descriptor; + + return Object.assign(descriptor, { + value(this: T, ...args: any[]) { + return fn({ + args, + instance: this, + callback: () => value?.apply(this, args), + }); + }, + }); + }; + + return result; +} + +type HashFunction = (...args: T) => string; + +/** + * The cache decorator parameters. + */ +interface CacheParameters { + /** + * The cache namespace + * Either a string or a hash function that generates a string + */ + namespace: string | HashFunction; + + /** + * The cache key + * Either a string or a hash function that generates a string + */ + key: string | HashFunction; + + /** + * The TTL (or expiry) of the key in minutes + */ + ttlMinutes?: number; +} + +/** + * caches the result of a decorated method. + */ +export function cache({ + namespace, + key, + ttlMinutes = 30, +}: CacheParameters): Decorator { + return decorate(async ({ args, instance, callback }) => { + let finalNamespace: string; + if (is.string(namespace)) { + finalNamespace = namespace; + } else if (is.function_(namespace)) { + finalNamespace = namespace.apply(instance, args); + } + + let finalKey: string; + if (is.string(key)) { + finalKey = key; + } else if (is.function_(key)) { + finalKey = key.apply(instance, args); + } + + const cachedResult = await packageCache.get( + finalNamespace, + finalKey + ); + + if (cachedResult !== undefined) { + return cachedResult; + } + + const result = await callback(); + + await packageCache.set(finalNamespace, finalKey, result, ttlMinutes); + return result; + }); +} diff --git a/lib/util/cache/package/index.ts b/lib/util/cache/package/index.ts index d1d5655b300532..de579bc0330a19 100644 --- a/lib/util/cache/package/index.ts +++ b/lib/util/cache/package/index.ts @@ -1,4 +1,4 @@ -import type { GlobalConfig } from '../../../config/types'; +import type { AllConfig } from '../../../config/types'; import * as memCache from '../memory'; import * as fileCache from './file'; import * as redisCache from './redis'; @@ -35,7 +35,7 @@ export function set( return cacheProxy.set(namespace, key, value, minutes); } -export function init(config: GlobalConfig): void { +export function init(config: AllConfig): void { if (config.redisUrl) { redisCache.init(config.redisUrl); cacheProxy = { @@ -51,7 +51,7 @@ export function init(config: GlobalConfig): void { } } -export function cleanup(config: GlobalConfig): void { +export function cleanup(config: AllConfig): void { if (config?.redisUrl) { redisCache.end(); } diff --git a/lib/util/cache/repository/types.ts b/lib/util/cache/repository/types.ts index 3a7995b559d728..bca06bd77c0a27 100644 --- a/lib/util/cache/repository/types.ts +++ b/lib/util/cache/repository/types.ts @@ -32,6 +32,8 @@ export interface BranchCache { } export interface Cache { + configFileName?: string; + semanticCommits?: 'enabled' | 'disabled'; branches?: BranchCache[]; repository?: string; revision?: number; diff --git a/lib/util/emoji.spec.ts b/lib/util/emoji.spec.ts index b8b15a9660c913..5c7083d7b284ce 100644 --- a/lib/util/emoji.spec.ts +++ b/lib/util/emoji.spec.ts @@ -1,22 +1,87 @@ +import { fromCodepointToUnicode, fromHexcodeToCodepoint } from 'emojibase'; import { getName } from '../../test/util'; -import { setEmojiConfig, unemojify } from './emoji'; +import { emojify, setEmojiConfig, stripEmojis, unemojify } from './emoji'; describe(getName(), () => { - it('strips emojis when the config has been set accordingly', () => { - const emoji = '🚀💎'; - const otherText = 'regular text'; - const text = `${emoji} ${otherText}`; - setEmojiConfig({ unicodeEmoji: false }); - const result = unemojify(text); - expect(result).not.toContain(emoji); + beforeEach(() => { + setEmojiConfig({ unicodeEmoji: true }); }); - it('does not strip emojis when the config demands it', () => { - const emoji = '🚀💎'; - const otherText = 'regular text'; - const text = `${emoji} ${otherText}`; - setEmojiConfig({ unicodeEmoji: true }); - const result = unemojify(text); - expect(result).toEqual(text); + describe('emojify', () => { + it('encodes known shortcodes', () => { + expect(emojify('Let it :bee:')).toEqual('Let it 🐝'); + }); + + it('encodes aliases', () => { + const bee = emojify(':bee:'); + const honeyBee = emojify(':honeybee:'); + expect(bee).toEqual(honeyBee); + }); + + it('omits unknown shortcodes', () => { + expect(emojify(':foo: :bar: :bee:')).toEqual(':foo: :bar: 🐝'); + }); + + it('does not encode when config option is disabled', () => { + setEmojiConfig({ unicodeEmoji: false }); + expect(emojify('Let it :bee:')).toEqual('Let it :bee:'); + }); + }); + + describe('unemojify', () => { + it('strips emojis when the config has been set accordingly', () => { + const emoji = '🚀💎'; + const otherText = 'regular text'; + const text = `${emoji} ${otherText}`; + setEmojiConfig({ unicodeEmoji: false }); + const result = unemojify(text); + expect(result).not.toContain(emoji); + }); + + it('does not strip emojis when the config demands it', () => { + const emoji = '🚀💎'; + const otherText = 'regular text'; + const text = `${emoji} ${otherText}`; + setEmojiConfig({ unicodeEmoji: true }); + const result = unemojify(text); + expect(result).toEqual(text); + }); + + describe('unsupported characters', () => { + const unsupported = 'đŸĒ†'; + + it('uses replacement character', () => { + setEmojiConfig({ unicodeEmoji: false }); + expect(unemojify(unsupported)).toEqual('īŋŊ'); + }); + }); + }); + + describe('problem characters', () => { + it.each(['🚀', '💎', '🧹', 'đŸ“Ļ'])('converts %s forth and back', (char) => { + setEmojiConfig({ unicodeEmoji: false }); + const codified = unemojify(char); + expect(codified).not.toEqual(char); + + setEmojiConfig({ unicodeEmoji: true }); + const emojified = emojify(codified); + expect(emojified).toEqual(char); + }); + }); + + describe('stripEmojis', () => { + const makeEmoji = (hexCode: string): string => + fromCodepointToUnicode(fromHexcodeToCodepoint(hexCode)); + + it('is independent of config option', () => { + const x: string = makeEmoji('26A0-FE0F'); + const y: string = makeEmoji('26A0'); + + setEmojiConfig({ unicodeEmoji: true }); + expect(stripEmojis(`foo ${x} bar`)).toEqual(`foo ${y} bar`); + + setEmojiConfig({ unicodeEmoji: false }); + expect(stripEmojis(`foo ${x} bar`)).toEqual(`foo ${y} bar`); + }); }); }); diff --git a/lib/util/emoji.ts b/lib/util/emoji.ts index ff21eed5aca9fe..9e2a8b73ea93e9 100644 --- a/lib/util/emoji.ts +++ b/lib/util/emoji.ts @@ -1,16 +1,103 @@ -import emoji from 'node-emoji'; +import is from '@sindresorhus/is'; +import mathiasBynensEmojiRegex from 'emoji-regex'; +import { + fromCodepointToUnicode, + fromHexcodeToCodepoint, + fromUnicodeToHexcode, +} from 'emojibase'; +import emojibaseEmojiRegex from 'emojibase-regex/emoji'; +import SHORTCODE_REGEX from 'emojibase-regex/shortcode'; import type { RenovateConfig } from '../config/types'; +import dataFiles from '../data-files.generated'; +import { regEx } from './regex'; let unicodeEmoji = true; +let mappingsInitialized = false; +const shortCodesByHex = new Map(); +const hexCodesByShort = new Map(); + +function lazyInitMappings(): void { + if (!mappingsInitialized) { + const table: Record = JSON.parse( + dataFiles.get('node_modules/emojibase-data/en/shortcodes/github.json') + ); + for (const [hex, val] of Object.entries(table)) { + const shortCodes: string[] = is.array(val) ? val : [val]; + shortCodesByHex.set(hex, `:${shortCodes[0]}:`); + shortCodes.forEach((shortCode) => { + hexCodesByShort.set(`:${shortCode}:`, hex); + }); + } + mappingsInitialized = true; + } +} + export function setEmojiConfig(_config: RenovateConfig): void { unicodeEmoji = _config.unicodeEmoji; } +const shortCodeRegex = regEx(SHORTCODE_REGEX.source, 'g'); + export function emojify(text: string): string { - return unicodeEmoji ? emoji.emojify(text) : text; + if (!unicodeEmoji) { + return text; + } + lazyInitMappings(); + return text.replace(shortCodeRegex, (shortCode) => { + const hexCode = hexCodesByShort.get(shortCode); + return hexCode + ? fromCodepointToUnicode(fromHexcodeToCodepoint(hexCode)) + : shortCode; + }); +} + +const emojiRegexSrc = [emojibaseEmojiRegex, mathiasBynensEmojiRegex()].map( + ({ source }) => source +); +const emojiRegex = new RegExp(`(?:${emojiRegexSrc.join('|')})`, 'g'); +const excludedModifiers = new Set([ + '20E3', + '200D', + 'FE0E', + 'FE0F', + '1F3FB', + '1F3FC', + '1F3FD', + '1F3FE', + '1F3FF', + '1F9B0', + '1F9B1', + '1F9B2', + '1F9B3', +]); + +export function stripHexCode(input: string): string { + return input + .split('-') + .filter((modifier) => !excludedModifiers.has(modifier)) + .join('-'); } export function unemojify(text: string): string { - return unicodeEmoji ? text : emoji.unemojify(text); + if (unicodeEmoji) { + return text; + } + lazyInitMappings(); + return text.replace(emojiRegex, (emoji) => { + const hexCode = stripHexCode(fromUnicodeToHexcode(emoji)); + const shortCode = shortCodesByHex.get(hexCode); + return shortCode || 'īŋŊ'; + }); +} + +function stripEmoji(emoji: string): string { + const hexCode = stripHexCode(fromUnicodeToHexcode(emoji)); + const codePoint = fromHexcodeToCodepoint(hexCode); + const result = fromCodepointToUnicode(codePoint); + return result; +} + +export function stripEmojis(input: string): string { + return input.replace(emojiRegex, stripEmoji); } diff --git a/lib/util/exec/__snapshots__/exec.spec.ts.snap b/lib/util/exec/__snapshots__/exec.spec.ts.snap index 68d144bc5f8d10..c309df433cfdf3 100644 --- a/lib/util/exec/__snapshots__/exec.spec.ts.snap +++ b/lib/util/exec/__snapshots__/exec.spec.ts.snap @@ -4,7 +4,6 @@ exports[`util/exec/exec Supports image prefetch 1`] = ` Array [ "echo hello", "echo hello", - "docker ps --filter label=renovate_child -aq", "docker pull renovate/image", "docker ps --filter name=renovate_image -aq", "docker run --rm --name=renovate_image --label=renovate_child renovate/image bash -l -c \\"echo hello\\"", @@ -12,7 +11,6 @@ Array [ "docker run --rm --name=renovate_image --label=renovate_child renovate/image bash -l -c \\"echo hello\\"", "echo hello", "echo hello", - "docker ps --filter label=renovate_child -aq", "docker ps --filter name=renovate_image -aq", "docker run --rm --name=renovate_image --label=renovate_child renovate/image bash -l -c \\"echo hello\\"", "docker ps --filter name=renovate_image -aq", diff --git a/lib/util/exec/common.ts b/lib/util/exec/common.ts index 3c09ad1947cccb..f0cb2a9c853624 100644 --- a/lib/util/exec/common.ts +++ b/lib/util/exec/common.ts @@ -6,15 +6,6 @@ import { promisify } from 'util'; export type Opt = T | null | undefined; -export enum BinarySource { - Docker = 'docker', - Global = 'global', -} - -export interface ExecConfig { - binarySource: Opt; -} - export type VolumesPair = [string, string]; export type VolumeOption = Opt; diff --git a/lib/util/exec/docker/index.ts b/lib/util/exec/docker/index.ts index b58a9d82d37c3a..5a619476be9eb6 100644 --- a/lib/util/exec/docker/index.ts +++ b/lib/util/exec/docker/index.ts @@ -7,7 +7,6 @@ import * as versioning from '../../../versioning'; import { ensureTrailingSlash } from '../../url'; import { DockerOptions, - ExecConfig, Opt, VolumeOption, VolumesPair, @@ -152,9 +151,14 @@ export async function removeDockerContainer( } // istanbul ignore next -export async function removeDanglingContainers(prefix: string): Promise { +export async function removeDanglingContainers(): Promise { + const { binarySource, dockerChildPrefix } = getAdminConfig(); + if (binarySource !== 'docker') { + return; + } + try { - const containerLabel = getContainerLabel(prefix); + const containerLabel = getContainerLabel(dockerChildPrefix); const res = await rawExec( `docker ps --filter label=${containerLabel} -aq`, { @@ -188,8 +192,7 @@ export async function removeDanglingContainers(prefix: string): Promise { export async function generateDockerCommand( commands: string[], - options: DockerOptions, - config: ExecConfig + options: DockerOptions ): Promise { const { envVars, cwd, tagScheme, tagConstraint } = options; let image = options.image; diff --git a/lib/util/exec/exec.spec.ts b/lib/util/exec/exec.spec.ts index b0b1c3cae367cf..b840a5fa7124a8 100644 --- a/lib/util/exec/exec.spec.ts +++ b/lib/util/exec/exec.spec.ts @@ -8,21 +8,15 @@ import { getName } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; import { TEMPORARY_ERROR } from '../../constants/error-messages'; -import { - BinarySource, - ExecConfig, - RawExecOptions, - VolumeOption, -} from './common'; +import { RawExecOptions, VolumeOption } from './common'; import * as dockerModule from './docker'; -import { ExecOptions, exec, setExecConfig } from '.'; +import { ExecOptions, exec } from '.'; const cpExec: jest.Mock = _cpExec as any; jest.mock('child_process'); interface TestInput { - execConfig: Partial; processEnv: Record; inCmd: string | string[]; inOpts: ExecOptions; @@ -40,8 +34,6 @@ describe(getName(), () => { const defaultCwd = `-w "${cwd}"`; const defaultVolumes = `-v "${cwd}":"${cwd}" -v "${cacheDir}":"${cacheDir}"`; - const execConfig = {}; - beforeEach(() => { dockerModule.resetPrefetchedImages(); jest.resetAllMocks(); @@ -82,7 +74,6 @@ describe(getName(), () => { [ 'Single command', { - execConfig, processEnv, inCmd, inOpts: {}, @@ -102,7 +93,6 @@ describe(getName(), () => { [ 'Multiple commands', { - execConfig, processEnv, inCmd: ['echo "begin"', inCmd, "echo 'end'"], inOpts: {}, @@ -136,7 +126,6 @@ describe(getName(), () => { [ 'Explicit env option', { - execConfig, processEnv, inCmd, inOpts: { env: { FOO: 'BAR' } }, @@ -156,7 +145,6 @@ describe(getName(), () => { [ 'Low trust level', { - execConfig, processEnv, inCmd, inOpts: {}, @@ -176,7 +164,6 @@ describe(getName(), () => { [ 'High trust level', { - execConfig, processEnv: envMock.full, inCmd, inOpts: {}, @@ -197,7 +184,6 @@ describe(getName(), () => { [ 'Docker', { - execConfig: { ...execConfig, binarySource: BinarySource.Docker }, processEnv, inCmd, inOpts: { docker, cwd }, @@ -217,13 +203,13 @@ describe(getName(), () => { maxBuffer: 10485760, }, ], + adminConfig: { binarySource: 'docker' }, }, ], [ 'Extra env vars', { - execConfig, processEnv, inCmd, inOpts: { @@ -244,13 +230,13 @@ describe(getName(), () => { maxBuffer: 10485760, }, ], + adminConfig: { binarySource: 'docker' }, }, ], [ 'Extra env vars (Docker)', { - execConfig: { ...execConfig, binarySource: BinarySource.Docker }, processEnv, inCmd, inOpts: { @@ -279,13 +265,13 @@ describe(getName(), () => { maxBuffer: 10485760, }, ], + adminConfig: { binarySource: 'docker' }, }, ], [ 'Extra env vars defaults', { - execConfig, processEnv: envMock.basic, inCmd, inOpts: { cwd, extraEnv: { SELECTED_ENV_VAR: 'Default value' } }, @@ -299,13 +285,13 @@ describe(getName(), () => { maxBuffer: 10485760, }, ], + adminConfig: { binarySource: 'docker' }, }, ], [ 'Extra env vars defaults (Docker)', { - execConfig: { ...execConfig, binarySource: BinarySource.Docker }, processEnv: envMock.basic, inCmd, inOpts: { @@ -329,13 +315,13 @@ describe(getName(), () => { maxBuffer: 10485760, }, ], + adminConfig: { binarySource: 'docker' }, }, ], [ 'Docker tags', { - execConfig: { ...execConfig, binarySource: BinarySource.Docker }, processEnv, inCmd, inOpts: { docker: { image, tag }, cwd }, @@ -355,13 +341,13 @@ describe(getName(), () => { maxBuffer: 10485760, }, ], + adminConfig: { binarySource: 'docker' }, }, ], [ 'Docker volumes', { - execConfig: { ...execConfig, binarySource: BinarySource.Docker }, processEnv, inCmd, inOpts: { cwd, docker: { image, volumes } }, @@ -381,16 +367,13 @@ describe(getName(), () => { maxBuffer: 10485760, }, ], + adminConfig: { binarySource: 'docker' }, }, ], [ 'Docker user', { - execConfig: { - ...execConfig, - binarySource: BinarySource.Docker, - }, processEnv, inCmd, inOpts: { docker }, @@ -410,17 +393,16 @@ describe(getName(), () => { maxBuffer: 10485760, }, ], - adminConfig: { dockerUser: 'foobar' }, + adminConfig: { + dockerUser: 'foobar', + binarySource: 'docker', + }, }, ], [ 'Docker image prefix', { - execConfig: { - ...execConfig, - binarySource: BinarySource.Docker, - }, processEnv, inCmd, inOpts: { docker }, @@ -440,17 +422,16 @@ describe(getName(), () => { maxBuffer: 10485760, }, ], - adminConfig: { dockerImagePrefix: 'ghcr.io/renovatebot' }, + adminConfig: { + dockerImagePrefix: 'ghcr.io/renovatebot', + binarySource: 'docker', + }, }, ], [ 'Docker child prefix', { - execConfig: { - ...execConfig, - binarySource: BinarySource.Docker, - }, processEnv, inCmd, inOpts: { docker }, @@ -470,17 +451,16 @@ describe(getName(), () => { maxBuffer: 10485760, }, ], - adminConfig: { dockerChildPrefix: 'myprefix_' }, + adminConfig: { + dockerChildPrefix: 'myprefix_', + binarySource: 'docker', + }, }, ], [ 'Docker extra commands', { - execConfig: { - ...execConfig, - binarySource: BinarySource.Docker, - }, processEnv, inCmd, inOpts: { @@ -506,16 +486,13 @@ describe(getName(), () => { maxBuffer: 10485760, }, ], + adminConfig: { binarySource: 'docker' }, }, ], [ 'Docker commands are nullable', { - execConfig: { - ...execConfig, - binarySource: BinarySource.Docker, - }, processEnv, inCmd, inOpts: { @@ -541,13 +518,13 @@ describe(getName(), () => { maxBuffer: 10485760, }, ], + adminConfig: { binarySource: 'docker' }, }, ], [ 'Explicit maxBuffer', { - execConfig, processEnv, inCmd, inOpts: { @@ -563,15 +540,13 @@ describe(getName(), () => { maxBuffer: 1024, }, ], + adminConfig: { binarySource: 'docker' }, }, ], [ 'Custom environment variables for child', { - execConfig: { - ...execConfig, - }, processEnv: envMock.basic, inCmd, inOpts: {}, @@ -589,6 +564,7 @@ describe(getName(), () => { customEnvVariables: { CUSTOM_KEY: 'CUSTOM_VALUE', }, + binarySource: 'docker', }, }, ], @@ -596,9 +572,6 @@ describe(getName(), () => { [ 'Custom environment variables for child should override', { - execConfig: { - ...execConfig, - }, processEnv: { ...envMock.basic, CUSTOM_KEY: 'CUSTOM_VALUE' }, inCmd, inOpts: {}, @@ -616,6 +589,7 @@ describe(getName(), () => { customEnvVariables: { CUSTOM_KEY: 'CUSTOM_OVERRIDEN_VALUE', }, + binarySource: 'docker', }, }, ], @@ -623,10 +597,6 @@ describe(getName(), () => { [ 'Custom environment variables for child (Docker)', { - execConfig: { - ...execConfig, - binarySource: BinarySource.Docker, - }, processEnv, inCmd, inOpts: { docker, cwd }, @@ -650,6 +620,7 @@ describe(getName(), () => { customEnvVariables: { CUSTOM_KEY: 'CUSTOM_VALUE', }, + binarySource: 'docker', }, }, ], @@ -657,10 +628,6 @@ describe(getName(), () => { [ 'Custom environment variables for child should override (Docker)', { - execConfig: { - ...execConfig, - binarySource: BinarySource.Docker, - }, processEnv: { ...envMock.basic, CUSTOM_KEY: 'CUSTOM_VALUE' }, inCmd, inOpts: { docker, cwd }, @@ -684,6 +651,7 @@ describe(getName(), () => { customEnvVariables: { CUSTOM_KEY: 'CUSTOM_OVERRIDEN_VALUE', }, + binarySource: 'docker', }, }, ], @@ -691,7 +659,6 @@ describe(getName(), () => { test.each(testInputs)('%s', async (_msg, testOpts) => { const { - execConfig: config, processEnv: procEnv, inCmd: cmd, inOpts, @@ -702,13 +669,6 @@ describe(getName(), () => { process.env = procEnv; - if (config) { - jest - .spyOn(dockerModule, 'removeDanglingContainers') - .mockResolvedValueOnce(); - await setExecConfig(config); - } - const actualCmd: string[] = []; const actualOpts: ChildProcessExecOptions[] = []; cpExec.mockImplementation((execCmd, execOpts, callback) => { @@ -734,19 +694,19 @@ describe(getName(), () => { return undefined; }); - await setExecConfig({ binarySource: BinarySource.Global }); + setAdminConfig({ binarySource: 'global' }); await exec(inCmd, { docker }); await exec(inCmd, { docker }); - await setExecConfig({ binarySource: BinarySource.Docker }); + setAdminConfig({ binarySource: 'docker' }); await exec(inCmd, { docker }); await exec(inCmd, { docker }); - await setExecConfig({ binarySource: BinarySource.Global }); + setAdminConfig({ binarySource: 'global' }); await exec(inCmd, { docker }); await exec(inCmd, { docker }); - await setExecConfig({ binarySource: BinarySource.Docker }); + setAdminConfig({ binarySource: 'docker' }); await exec(inCmd, { docker }); await exec(inCmd, { docker }); @@ -769,10 +729,7 @@ describe(getName(), () => { }); it('wraps error if removeDockerContainer throws an error', async () => { - cpExec.mockImplementationOnce((_execCmd, _execOpts, callback) => - callback(null, { stdout: '', stderr: '' }) - ); - await setExecConfig({ binarySource: BinarySource.Docker }); + setAdminConfig({ binarySource: 'docker' }); cpExec.mockImplementation(() => { throw new Error('some error occurred'); }); diff --git a/lib/util/exec/index.ts b/lib/util/exec/index.ts index a01b886e5e0d83..991ec87ae713e7 100644 --- a/lib/util/exec/index.ts +++ b/lib/util/exec/index.ts @@ -1,41 +1,18 @@ import type { ExecOptions as ChildProcessExecOptions } from 'child_process'; import { dirname, join } from 'upath'; import { getAdminConfig } from '../../config/admin'; -import type { RenovateConfig } from '../../config/types'; import { TEMPORARY_ERROR } from '../../constants/error-messages'; import { logger } from '../../logger'; import { - BinarySource, DockerOptions, - ExecConfig, ExecResult, Opt, RawExecOptions, rawExec, } from './common'; -import { - generateDockerCommand, - removeDanglingContainers, - removeDockerContainer, -} from './docker'; +import { generateDockerCommand, removeDockerContainer } from './docker'; import { getChildProcessEnv } from './env'; -const execConfig: ExecConfig = { - binarySource: null, -}; - -export async function setExecConfig( - config: Partial -): Promise { - for (const key of Object.keys(execConfig)) { - const value = config[key]; - execConfig[key] = value || null; - } - if (execConfig.binarySource === 'docker') { - await removeDanglingContainers(getAdminConfig().dockerChildPrefix); - } -} - type ExtraEnv = Record; export interface ExecOptions extends ChildProcessExecOptions { @@ -93,11 +70,11 @@ export async function exec( opts: ExecOptions = {} ): Promise { const { env, docker, cwdFile } = opts; - const { dockerChildPrefix, customEnvVariables } = getAdminConfig(); + const { binarySource, dockerChildPrefix, customEnvVariables, localDir } = + getAdminConfig(); const extraEnv = { ...opts.extraEnv, ...customEnvVariables }; let cwd; // istanbul ignore if - const { localDir } = getAdminConfig(); if (cwdFile) { cwd = join(localDir, dirname(cwdFile)); } @@ -121,7 +98,7 @@ export async function exec( rawExecOptions.maxBuffer = rawExecOptions.maxBuffer || 10 * 1024 * 1024; let commands = typeof cmd === 'string' ? [cmd] : cmd; - const useDocker = execConfig.binarySource === BinarySource.Docker && docker; + const useDocker = binarySource === 'docker' && docker; if (useDocker) { logger.debug('Using docker to execute'); const dockerOptions = { @@ -130,11 +107,7 @@ export async function exec( envVars: dockerEnvVars(extraEnv, childEnv), }; - const dockerCommand = await generateDockerCommand( - commands, - dockerOptions, - execConfig - ); + const dockerCommand = await generateDockerCommand(commands, dockerOptions); commands = [dockerCommand]; } @@ -149,7 +122,7 @@ export async function exec( try { res = await rawExec(rawExecCommand, rawExecOptions); } catch (err) { - logger.trace({ err }, 'rawExec err'); + logger.debug({ err }, 'rawExec err'); if (useDocker) { await removeDockerContainer(docker.image, dockerChildPrefix).catch( (removeErr: Error) => { diff --git a/lib/util/fs/__snapshots__/index.spec.ts.snap b/lib/util/fs/__snapshots__/index.spec.ts.snap new file mode 100644 index 00000000000000..de76c6eab813d8 --- /dev/null +++ b/lib/util/fs/__snapshots__/index.spec.ts.snap @@ -0,0 +1,16 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`util/fs/index readLocalDirectory returns dir content 1`] = ` +Array [ + "Cargo.lock", + "Cargo.toml", +] +`; + +exports[`util/fs/index readLocalDirectory returns dir content 2`] = ` +Array [ + "Cargo.lock", + "Cargo.toml", + "subdir", +] +`; diff --git a/lib/util/fs/index.spec.ts b/lib/util/fs/index.spec.ts index b8a05915e28bc5..e619dd32a5e3fb 100644 --- a/lib/util/fs/index.spec.ts +++ b/lib/util/fs/index.spec.ts @@ -2,9 +2,11 @@ import { withDir } from 'tmp-promise'; import { getName } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import { + ensureLocalDir, findLocalSiblingOrParent, getSubDirectory, localPathExists, + readLocalDirectory, readLocalFile, writeLocalFile, } from '.'; @@ -99,4 +101,55 @@ describe(getName(), () => { expect(await findLocalSiblingOrParent('other', '/etc/hosts')).toBeNull(); }); }); + + describe('readLocalDirectory', () => { + it('returns dir content', async () => { + await withDir( + async (localDir) => { + setAdminConfig({ + localDir: localDir.path, + }); + await writeLocalFile('test/Cargo.toml', ''); + await writeLocalFile('test/Cargo.lock', ''); + + const result = await readLocalDirectory('test'); + expect(result).not.toBeNull(); + expect(result).toBeArrayOfSize(2); + expect(result).toMatchSnapshot(); + + await writeLocalFile('Cargo.lock', ''); + await writeLocalFile('/test/subdir/Cargo.lock', ''); + + const resultWithAdditionalFiles = await readLocalDirectory('test'); + expect(resultWithAdditionalFiles).not.toBeNull(); + expect(resultWithAdditionalFiles).toBeArrayOfSize(3); + expect(resultWithAdditionalFiles).toMatchSnapshot(); + }, + { + unsafeCleanup: true, + } + ); + }); + + it('return empty array for non existing directory', async () => { + await withDir( + async (localDir) => { + setAdminConfig({ + localDir: localDir.path, + }); + await expect(readLocalDirectory('somedir')).rejects.toThrow(); + }, + { + unsafeCleanup: true, + } + ); + }); + + it('return empty array for a existing but empty directory', async () => { + await ensureLocalDir('somedir'); + const result = await readLocalDirectory('somedir'); + expect(result).not.toBeNull(); + expect(result).toBeArrayOfSize(0); + }); + }); }); diff --git a/lib/util/fs/index.ts b/lib/util/fs/index.ts index be10caffd13b34..3c0938380d9c9c 100644 --- a/lib/util/fs/index.ts +++ b/lib/util/fs/index.ts @@ -1,3 +1,5 @@ +import stream from 'stream'; +import util from 'util'; import is from '@sindresorhus/is'; import * as fs from 'fs-extra'; import { isAbsolute, join, parse } from 'upath'; @@ -6,6 +8,8 @@ import { logger } from '../../logger'; export * from './proxies'; +export const pipeline = util.promisify(stream.pipeline); + export function getSubDirectory(fileName: string): string { return parse(fileName).dir; } @@ -136,3 +140,17 @@ export async function findLocalSiblingOrParent( return null; } + +/** + * Get files by name from directory + */ +export async function readLocalDirectory(path: string): Promise { + const { localDir } = getAdminConfig(); + const localPath = join(localDir, path); + const fileList = await fs.readdir(localPath); + return fileList; +} + +export function createWriteStream(path: string): fs.WriteStream { + return fs.createWriteStream(path); +} diff --git a/lib/util/fs/proxies.ts b/lib/util/fs/proxies.ts index 9dfefc4667c167..3abe0f4624a97c 100644 --- a/lib/util/fs/proxies.ts +++ b/lib/util/fs/proxies.ts @@ -70,3 +70,21 @@ export function move( ): Promise { return fs.move(src, dest, options ?? {}); } + +// istanbul ignore next +export function readdir(path: string): Promise { + return fs.readdir(path); +} + +// istanbul ignore next +export function rm( + path: string, + options?: { + force?: boolean; + maxRetries?: number; + recursive?: boolean; + retryDelay?: number; + } +): Promise { + return fs.rm(path, options); +} diff --git a/lib/util/git/__snapshots__/index.spec.ts.snap b/lib/util/git/__snapshots__/index.spec.ts.snap index 570893090636d5..b3b416d2b00078 100644 --- a/lib/util/git/__snapshots__/index.spec.ts.snap +++ b/lib/util/git/__snapshots__/index.spec.ts.snap @@ -41,5 +41,6 @@ exports[`util/git/index initRepo()) should fetch latest 2`] = ` Array [ "past message2", "master message", + "past message", ] `; diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts index 1420f18a5c1ce8..a3f2a9fc49c01b 100644 --- a/lib/util/git/index.ts +++ b/lib/util/git/index.ts @@ -265,7 +265,9 @@ export async function syncGit(): Promise { if (await fs.exists(gitHead)) { try { await git.raw(['remote', 'set-url', 'origin', config.url]); + await resetToBranch(await getDefaultBranch(git)); const fetchStart = Date.now(); + await git.pull(); await git.fetch(['--depth=10']); config.currentBranch = config.currentBranch || (await getDefaultBranch(git)); @@ -273,13 +275,13 @@ export async function syncGit(): Promise { await cleanLocalBranches(); await git.raw(['remote', 'prune', 'origin']); const durationMs = Math.round(Date.now() - fetchStart); - logger.debug({ durationMs }, 'git fetch completed'); + logger.info({ durationMs }, 'git fetch completed'); clone = false; } catch (err) /* istanbul ignore next */ { if (err.message === REPOSITORY_EMPTY) { throw err; } - logger.warn({ err }, 'git fetch error'); + logger.info({ err }, 'git fetch error'); } } if (clone) { @@ -371,7 +373,7 @@ async function syncBranch(branchName: string): Promise { // fetch the branch only if it's not part of the existing branchPrefix try { await git.raw(['remote', 'set-branches', '--add', 'origin', branchName]); - await git.fetch(['origin', branchName, '--depth=2']); + await git.fetch(['origin', branchName, '--depth=5']); } catch (err) /* istanbul ignore next */ { checkForPlatformFailure(err); } @@ -783,7 +785,7 @@ export async function commitFiles({ logger.debug({ result: pushRes }, 'git push'); // Fetch it after create const ref = `refs/heads/${branchName}:refs/remotes/origin/${branchName}`; - await git.fetch(['origin', ref, '--depth=2', '--force']); + await git.fetch(['origin', ref, '--depth=5', '--force']); config.branchCommits[branchName] = ( await git.revparse([branchName]) ).trim(); diff --git a/lib/util/http/__snapshots__/gitlab.spec.ts.snap b/lib/util/http/__snapshots__/gitlab.spec.ts.snap index 49f1f886195b9f..928048947c541c 100644 --- a/lib/util/http/__snapshots__/gitlab.spec.ts.snap +++ b/lib/util/http/__snapshots__/gitlab.spec.ts.snap @@ -54,6 +54,44 @@ Array [ ] `; +exports[`util/http/gitlab paginates with GITLAB_IGNORE_REPO_URL set 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer abc123", + "host": "mycompany.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://mycompany.com/gitlab/api/v4/some-url", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer abc123", + "host": "mycompany.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://mycompany.com/gitlab/api/v4/some-url&page=2", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer abc123", + "host": "mycompany.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://mycompany.com/gitlab/api/v4/some-url&page=3", + }, +] +`; + exports[`util/http/gitlab posts 1`] = ` Array [ Object { diff --git a/lib/util/http/bitbucket-server.spec.ts b/lib/util/http/bitbucket-server.spec.ts index f93832f00b35cd..149f6fe182c731 100644 --- a/lib/util/http/bitbucket-server.spec.ts +++ b/lib/util/http/bitbucket-server.spec.ts @@ -22,14 +22,9 @@ describe(getName(), () => { token: 'token', }); - httpMock.reset(); - httpMock.setup(); - setBaseUrl(baseUrl); }); - afterEach(() => { - httpMock.reset(); - }); + it('posts', async () => { const body = ['a', 'b']; httpMock.scope(baseUrl).post('/some-url').reply(200, body); diff --git a/lib/util/http/bitbucket.spec.ts b/lib/util/http/bitbucket.spec.ts index 3c17426d8e8965..377d96387b8ecd 100644 --- a/lib/util/http/bitbucket.spec.ts +++ b/lib/util/http/bitbucket.spec.ts @@ -22,14 +22,9 @@ describe(getName(), () => { token: 'token', }); - httpMock.reset(); - httpMock.setup(); - setBaseUrl(baseUrl); }); - afterEach(() => { - httpMock.reset(); - }); + it('posts', async () => { const body = ['a', 'b']; httpMock.scope(baseUrl).post('/some-url').reply(200, body); diff --git a/lib/util/http/gitea.spec.ts b/lib/util/http/gitea.spec.ts index 6f28781cd9ff0c..b3043b9bc1bc3d 100644 --- a/lib/util/http/gitea.spec.ts +++ b/lib/util/http/gitea.spec.ts @@ -12,16 +12,9 @@ describe(getName(), () => { jest.resetAllMocks(); - httpMock.reset(); - httpMock.setup(); - setBaseUrl(baseUrl); }); - afterEach(() => { - httpMock.reset(); - }); - it('supports responses without pagination when enabled', async () => { httpMock .scope(baseUrl) diff --git a/lib/util/http/github.spec.ts b/lib/util/http/github.spec.ts index b1f80a7c7236cd..210872ab0be7c9 100644 --- a/lib/util/http/github.spec.ts +++ b/lib/util/http/github.spec.ts @@ -1,4 +1,3 @@ -import nock from 'nock'; import * as httpMock from '../../../test/http-mock'; import { getName } from '../../../test/util'; import { @@ -19,11 +18,9 @@ describe(getName(), () => { githubApi = new GithubHttp(); setBaseUrl(githubApiHost); jest.resetAllMocks(); - httpMock.setup(); }); afterEach(() => { - httpMock.reset(); hostRules.clear(); }); @@ -107,7 +104,7 @@ describe(getName(), () => { async function fail( code: number, body: any = undefined, - headers: nock.ReplyHeaders = undefined + headers: httpMock.ReplyHeaders = undefined ) { const url = '/some-url'; httpMock diff --git a/lib/util/http/gitlab.spec.ts b/lib/util/http/gitlab.spec.ts index 25b03855dac901..fff760d17da425 100644 --- a/lib/util/http/gitlab.spec.ts +++ b/lib/util/http/gitlab.spec.ts @@ -11,6 +11,7 @@ hostRules.add({ }); const gitlabApiHost = 'https://gitlab.com'; +const selfHostedUrl = 'http://mycompany.com/gitlab'; describe(getName(), () => { let gitlabApi: GitlabHttp; @@ -18,12 +19,11 @@ describe(getName(), () => { beforeEach(() => { gitlabApi = new GitlabHttp(); setBaseUrl(`${gitlabApiHost}/api/v4/`); - httpMock.setup(); + delete process.env.GITLAB_IGNORE_REPO_URL; }); afterEach(() => { jest.resetAllMocks(); - httpMock.reset(); }); it('paginates', async () => { @@ -46,6 +46,29 @@ describe(getName(), () => { expect(trace).toHaveLength(3); expect(trace).toMatchSnapshot(); }); + it('paginates with GITLAB_IGNORE_REPO_URL set', async () => { + process.env.GITLAB_IGNORE_REPO_URL = 'true'; + setBaseUrl(`${selfHostedUrl}/api/v4/`); + + httpMock + .scope(selfHostedUrl) + .get('/api/v4/some-url') + .reply(200, ['a'], { + link: '; rel="next", ; rel="last"', + }) + .get('/api/v4/some-url&page=2') + .reply(200, ['b', 'c'], { + link: '; rel="next", ; rel="last"', + }) + .get('/api/v4/some-url&page=3') + .reply(200, ['d']); + const res = await gitlabApi.getJson('some-url', { paginate: true }); + expect(res.body).toHaveLength(4); + + const trace = httpMock.getTrace(); + expect(trace).toHaveLength(3); + expect(trace).toMatchSnapshot(); + }); it('attempts to paginate', async () => { httpMock.scope(gitlabApiHost).get('/api/v4/some-url').reply(200, ['a'], { link: '; rel="last"', @@ -65,9 +88,7 @@ describe(getName(), () => { expect(httpMock.getTrace()).toMatchSnapshot(); }); it('sets baseUrl', () => { - expect(() => - setBaseUrl('https://gitlab.renovatebot.com/api/v4/') - ).not.toThrow(); + expect(() => setBaseUrl(`${selfHostedUrl}/api/v4/`)).not.toThrow(); }); describe('fails with', () => { diff --git a/lib/util/http/gitlab.ts b/lib/util/http/gitlab.ts index 13bd3028171aea..f93df7358a45e9 100644 --- a/lib/util/http/gitlab.ts +++ b/lib/util/http/gitlab.ts @@ -2,6 +2,7 @@ import parseLinkHeader from 'parse-link-header'; import { PLATFORM_TYPE_GITLAB } from '../../constants/platforms'; import { logger } from '../../logger'; import { ExternalHostError } from '../../types/errors/external-host-error'; +import { parseUrl } from '../url'; import { Http, HttpResponse, InternalHttpOptions } from '.'; let baseUrl = 'https://gitlab.com/api/v4/'; @@ -42,8 +43,15 @@ export class GitlabHttp extends Http { try { const linkHeader = parseLinkHeader(result.headers.link as string); if (linkHeader?.next) { + const nextUrl = parseUrl(linkHeader.next.url); + if (process.env.GITLAB_IGNORE_REPO_URL) { + const defaultEndpoint = new URL(baseUrl); + nextUrl.protocol = defaultEndpoint.protocol; + nextUrl.host = defaultEndpoint.host; + } + result.body = result.body.concat( - (await this.request(linkHeader.next.url, opts)).body + (await this.request(nextUrl, opts)).body ); } } catch (err) /* istanbul ignore next */ { diff --git a/lib/util/http/host-rules.spec.ts b/lib/util/http/host-rules.spec.ts index f450f61bbae4bf..46d584e6b802c9 100644 --- a/lib/util/http/host-rules.spec.ts +++ b/lib/util/http/host-rules.spec.ts @@ -1,4 +1,3 @@ -import * as httpMock from '../../../test/http-mock'; import { getName } from '../../../test/util'; import { PLATFORM_TYPE_GITEA, @@ -38,14 +37,10 @@ describe(getName(), () => { authType: 'Basic', token: 'XXX', }); - - httpMock.reset(); - httpMock.setup(); }); afterEach(() => { delete process.env.HTTP_PROXY; - httpMock.reset(); }); it('adds token', () => { diff --git a/lib/util/http/index.spec.ts b/lib/util/http/index.spec.ts index 8915a8a6dd3e2d..846fd929695ebc 100644 --- a/lib/util/http/index.spec.ts +++ b/lib/util/http/index.spec.ts @@ -1,4 +1,4 @@ -import nock from 'nock'; +import * as httpMock from '../../../test/http-mock'; import { getName } from '../../../test/util'; import { EXTERNAL_HOST_ERROR, @@ -15,29 +15,28 @@ describe(getName(), () => { beforeEach(() => { http = new Http('dummy'); - nock.cleanAll(); hostRules.clear(); queue.clear(); }); it('get', async () => { - nock(baseUrl).get('/test').reply(200); + httpMock.scope(baseUrl).get('/test').reply(200); expect(await http.get('http://renovate.com/test')).toMatchSnapshot(); - expect(nock.isDone()).toBe(true); + expect(httpMock.allUsed()).toBe(true); }); it('returns 429 error', async () => { - nock(baseUrl).get('/test').reply(429); + httpMock.scope(baseUrl).get('/test').reply(429); await expect(http.get('http://renovate.com/test')).rejects.toThrow( 'Response code 429 (Too Many Requests)' ); - expect(nock.isDone()).toBe(true); + expect(httpMock.allUsed()).toBe(true); }); it('converts 404 error to ExternalHostError', async () => { - nock(baseUrl).get('/test').reply(404); + httpMock.scope(baseUrl).get('/test').reply(404); hostRules.add({ abortOnError: true }); await expect(http.get('http://renovate.com/test')).rejects.toThrow( EXTERNAL_HOST_ERROR ); - expect(nock.isDone()).toBe(true); + expect(httpMock.allUsed()).toBe(true); }); it('disables hosts', async () => { hostRules.add({ matchHost: 'renovate.com', enabled: false }); @@ -46,55 +45,55 @@ describe(getName(), () => { ); }); it('ignores 404 error and does not throw ExternalHostError', async () => { - nock(baseUrl).get('/test').reply(404); + httpMock.scope(baseUrl).get('/test').reply(404); hostRules.add({ abortOnError: true, abortIgnoreStatusCodes: [404] }); await expect(http.get('http://renovate.com/test')).rejects.toThrow( 'Response code 404 (Not Found)' ); - expect(nock.isDone()).toBe(true); + expect(httpMock.allUsed()).toBe(true); }); it('getJson', async () => { - nock(baseUrl).get('/').reply(200, '{ "test": true }'); + httpMock.scope(baseUrl).get('/').reply(200, '{ "test": true }'); expect(await http.getJson('http://renovate.com')).toMatchSnapshot(); }); it('postJson', async () => { - nock(baseUrl).post('/').reply(200, {}); + httpMock.scope(baseUrl).post('/').reply(200, {}); expect( await http.postJson('http://renovate.com', { body: {}, baseUrl }) ).toMatchSnapshot(); - expect(nock.isDone()).toBe(true); + expect(httpMock.allUsed()).toBe(true); }); it('putJson', async () => { - nock(baseUrl).put('/').reply(200, {}); + httpMock.scope(baseUrl).put('/').reply(200, {}); expect( await http.putJson('http://renovate.com', { body: {}, baseUrl }) ).toMatchSnapshot(); - expect(nock.isDone()).toBe(true); + expect(httpMock.allUsed()).toBe(true); }); it('patchJson', async () => { - nock(baseUrl).patch('/').reply(200, {}); + httpMock.scope(baseUrl).patch('/').reply(200, {}); expect( await http.patchJson('http://renovate.com', { body: {}, baseUrl }) ).toMatchSnapshot(); - expect(nock.isDone()).toBe(true); + expect(httpMock.allUsed()).toBe(true); }); it('deleteJson', async () => { - nock(baseUrl).delete('/').reply(200, {}); + httpMock.scope(baseUrl).delete('/').reply(200, {}); expect( await http.deleteJson('http://renovate.com', { body: {}, baseUrl }) ).toMatchSnapshot(); - expect(nock.isDone()).toBe(true); + expect(httpMock.allUsed()).toBe(true); }); it('headJson', async () => { - nock(baseUrl).head('/').reply(200, {}); + httpMock.scope(baseUrl).head('/').reply(200, {}); expect( await http.headJson('http://renovate.com', { baseUrl }) ).toMatchSnapshot(); - expect(nock.isDone()).toBe(true); + expect(httpMock.allUsed()).toBe(true); }); it('stream', async () => { - nock(baseUrl).get('/some').reply(200, {}); + httpMock.scope(baseUrl).get('/some').reply(200, {}); const stream = http.stream('/some', { baseUrl, @@ -115,20 +114,21 @@ describe(getName(), () => { await done; expect(data).toBe('{}'); - expect(nock.isDone()).toBe(true); + expect(httpMock.allUsed()).toBe(true); }); it('retries', async () => { const NODE_ENV = process.env.NODE_ENV; try { delete process.env.NODE_ENV; - nock(baseUrl) + httpMock + .scope(baseUrl) .head('/') .reply(500) .head('/') .reply(200, undefined, { 'x-some-header': 'abc' }); expect(await http.head('http://renovate.com')).toMatchSnapshot(); - expect(nock.isDone()).toBe(true); + expect(httpMock.allUsed()).toBe(true); } finally { process.env.NODE_ENV = NODE_ENV; } @@ -158,7 +158,8 @@ describe(getName(), () => { const [fooReq, fooStart, fooResp, fooFinish] = mockRequestResponse(); const [barReq, barStart, barResp, barFinish] = mockRequestResponse(); - nock(baseUrl) + httpMock + .scope(baseUrl) .get('/foo') .reply(200, () => { foo = true; diff --git a/lib/util/package-rules.spec.ts b/lib/util/package-rules.spec.ts index ded6c2fe17e3c2..5bd2d52ae0656b 100644 --- a/lib/util/package-rules.spec.ts +++ b/lib/util/package-rules.spec.ts @@ -7,7 +7,7 @@ import { } from '../constants/languages'; import * as datasourceDocker from '../datasource/docker'; -import * as datasourceOrb from '../datasource/orb'; +import { OrbDatasource } from '../datasource/orb'; import { applyPackageRules } from './package-rules'; type TestConfig = PackageRuleInputConfig & { @@ -318,14 +318,14 @@ describe('applyPackageRules()', () => { const config: TestConfig = { packageRules: [ { - matchDatasources: [datasourceOrb.id, datasourceDocker.id], + matchDatasources: [OrbDatasource.id, datasourceDocker.id], x: 1, }, ], }; const dep = { depType: 'dependencies', - datasource: datasourceOrb.id, + datasource: OrbDatasource.id, baseBranch: 'master', }; const res = applyPackageRules({ ...config, ...dep }); @@ -342,7 +342,7 @@ describe('applyPackageRules()', () => { }; const dep = { depType: 'dependencies', - datasource: datasourceOrb.id, + datasource: OrbDatasource.id, baseBranch: 'master', }; const res = applyPackageRules({ ...config, ...dep }); @@ -352,7 +352,7 @@ describe('applyPackageRules()', () => { const config: TestConfig = { packageRules: [ { - matchDatasources: [datasourceOrb.id], + matchDatasources: [OrbDatasource.id], x: 1, }, ], diff --git a/lib/versioning/index.ts b/lib/versioning/index.ts index da12a3de646f4b..a192f8353833cb 100644 --- a/lib/versioning/index.ts +++ b/lib/versioning/index.ts @@ -16,7 +16,7 @@ export const getVersionings = (): Map< export function get(versioning: string): VersioningApi { if (!versioning) { - logger.debug('Missing versioning'); + logger.trace('Missing versioning, using semver as fallback.'); return versionings.get('semver') as VersioningApi; } let versioningName: string; diff --git a/lib/versioning/maven/compare.ts b/lib/versioning/maven/compare.ts index 4e13d83ea106bf..cf3980772a6db2 100644 --- a/lib/versioning/maven/compare.ts +++ b/lib/versioning/maven/compare.ts @@ -295,7 +295,7 @@ function isVersion(version: string): boolean { const INCLUDING_POINT = 'INCLUDING_POINT'; const EXCLUDING_POINT = 'EXCLUDING_POINT'; -function parseRange(rangeStr: string): any { +function parseRange(rangeStr: string): Range[] { function emptyInterval(): Range { return { leftType: null, @@ -499,24 +499,20 @@ function autoExtendMavenRange( if (isPoint(range)) { return `[${newValue}]`; } - let nearestIntervalIdx = 0; - const len = range.length; - for (let idx = len - 1; idx >= 0; idx = -1) { - const { leftValue, rightValue } = range[idx]; - if (rightValue === null) { - nearestIntervalIdx = idx; - break; - } - if (compare(rightValue, newValue) === -1) { - nearestIntervalIdx = idx; - break; - } - if (leftValue && compare(leftValue, newValue) !== 1) { - return currentRepresentation; - } + + const interval = [...range].reverse().find((elem) => { + const { rightType, rightValue } = elem; + return ( + rightValue === null || + (rightType === INCLUDING_POINT && compare(rightValue, newValue) === -1) || + (rightType === EXCLUDING_POINT && compare(rightValue, newValue) !== 1) + ); + }); + + if (!interval) { + return currentRepresentation; } - const interval = range[nearestIntervalIdx]; const { leftValue, rightValue } = interval; if ( leftValue !== null && @@ -545,13 +541,6 @@ function autoExtendMavenRange( interval.leftValue = coerceRangeValue(leftValue, newValue); } - if (interval.leftValue && interval.rightValue) { - const correctRepresentation = - compare(interval.leftValue, interval.rightValue) === 1 - ? null - : rangeToStr(range); - return correctRepresentation || currentRepresentation; - } return rangeToStr(range); } diff --git a/lib/versioning/maven/index.spec.ts b/lib/versioning/maven/index.spec.ts index 537d8ba7958aab..8ca9826afa8557 100644 --- a/lib/versioning/maven/index.spec.ts +++ b/lib/versioning/maven/index.spec.ts @@ -279,6 +279,17 @@ describe(getName(), () => { ['(,1.0]', '2.0.0', '(,2.0]'], ['(,1]', '2.0.0', '(,2]'], ['(,1.0.0-foobar]', '2.0.0', '(,2.0.0]'], + + ['[1,2]', '2', '[1,2]'], + ['[1,2)', '2', '[2,3)'], + ['[0,2)', '2', '[0,3)'], + ['[1.2,1.3]', '1.3', '[1.2,1.3]'], + ['[1.2,1.3)', '1.3', '[1.3,1.4)'], + ['[1.1,1.3)', '1.3', '[1.1,1.4)'], + ['[1.2.3,1.2.4]', '1.2.4', '[1.2.3,1.2.4]'], + ['[1.2.3,1.2.4)', '1.2.4', '[1.2.4,1.2.5)'], + ['[1.2.1,1.2.4)', '1.2.4', '[1.2.1,1.2.5)'], + ['[1,1.2.3)', '1.2.3', '[1,1.2.4)'], ]; sample.forEach(([oldRepr, newValue, newRepr]) => { expect(autoExtendMavenRange(oldRepr, newValue)).toEqual(newRepr); diff --git a/lib/workers/branch/__snapshots__/index.spec.ts.snap b/lib/workers/branch/__snapshots__/index.spec.ts.snap index 596958e7a2ec4a..567b322f4c17af 100644 --- a/lib/workers/branch/__snapshots__/index.spec.ts.snap +++ b/lib/workers/branch/__snapshots__/index.spec.ts.snap @@ -3,6 +3,7 @@ exports[`workers/branch/index processBranch branch pr no rebase (dry run) 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "pr-edited", } `; @@ -10,6 +11,7 @@ Object { exports[`workers/branch/index processBranch branch pr no schedule (dry run) 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "done", } `; @@ -17,6 +19,7 @@ Object { exports[`workers/branch/index processBranch branch pr no schedule 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "done", } `; @@ -24,6 +27,7 @@ Object { exports[`workers/branch/index processBranch branch pr no schedule lockfile (dry run) 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "done", } `; @@ -31,6 +35,7 @@ Object { exports[`workers/branch/index processBranch closed pr (dry run) 1`] = ` Object { "branchExists": false, + "prNo": undefined, "result": "already-existed", } `; @@ -38,6 +43,7 @@ Object { exports[`workers/branch/index processBranch continues branch if branch edited and but PR found 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "error", } `; @@ -45,6 +51,7 @@ Object { exports[`workers/branch/index processBranch does not skip branch if edited PR found with rebaseLabel 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "error", } `; @@ -52,6 +59,7 @@ Object { exports[`workers/branch/index processBranch executes post-upgrade tasks if trust is high 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "done", } `; @@ -59,6 +67,7 @@ Object { exports[`workers/branch/index processBranch executes post-upgrade tasks once when set to branch mode 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "done", } `; @@ -66,6 +75,7 @@ Object { exports[`workers/branch/index processBranch executes post-upgrade tasks with disabled post-upgrade command templating 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "done", } `; @@ -73,6 +83,31 @@ Object { exports[`workers/branch/index processBranch executes post-upgrade tasks with multiple dependecy in one branch 1`] = ` Object { "branchExists": true, + "prNo": undefined, + "result": "done", +} +`; + +exports[`workers/branch/index processBranch handles unknown PrBlockedBy 1`] = ` +Object { + "branchExists": true, + "prBlockedBy": "whoops", + "result": "error", +} +`; + +exports[`workers/branch/index processBranch returns if PR creation failed 1`] = ` +Object { + "branchExists": true, + "prBlockedBy": "Error", + "result": "error", +} +`; + +exports[`workers/branch/index processBranch returns if branch automerge is pending 1`] = ` +Object { + "branchExists": true, + "prBlockedBy": "BranchAutomerge", "result": "done", } `; @@ -80,6 +115,7 @@ Object { exports[`workers/branch/index processBranch returns if branch creation limit exceeded 1`] = ` Object { "branchExists": false, + "prNo": undefined, "result": "branch-limit-reached", } `; @@ -87,6 +123,7 @@ Object { exports[`workers/branch/index processBranch returns if branch exists and prCreation set to approval 1`] = ` Object { "branchExists": true, + "prBlockedBy": "NeedsApproval", "result": "needs-pr-approval", } `; @@ -94,6 +131,7 @@ Object { exports[`workers/branch/index processBranch returns if branch exists but pending 1`] = ` Object { "branchExists": true, + "prBlockedBy": "AwaitingTests", "result": "pending", } `; @@ -101,6 +139,7 @@ Object { exports[`workers/branch/index processBranch returns if branch exists but updated 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "pending", } `; @@ -108,6 +147,7 @@ Object { exports[`workers/branch/index processBranch returns if commit limit exceeded 1`] = ` Object { "branchExists": false, + "prNo": undefined, "result": "commit-limit-reached", } `; @@ -115,6 +155,7 @@ Object { exports[`workers/branch/index processBranch returns if no work 1`] = ` Object { "branchExists": false, + "prNo": undefined, "result": "no-work", } `; @@ -122,6 +163,7 @@ Object { exports[`workers/branch/index processBranch returns if pending checks 1`] = ` Object { "branchExists": false, + "prNo": undefined, "result": "pending", } `; @@ -129,6 +171,7 @@ Object { exports[`workers/branch/index processBranch returns if pr creation limit exceeded and branch exists 1`] = ` Object { "branchExists": true, + "prBlockedBy": "RateLimited", "result": "pr-limit-reached", } `; @@ -136,6 +179,7 @@ Object { exports[`workers/branch/index processBranch skips branch for fresh release with stabilityDays 1`] = ` Object { "branchExists": false, + "prNo": undefined, "result": "pending", } `; @@ -143,6 +187,7 @@ Object { exports[`workers/branch/index processBranch skips branch if branch edited and and PR found with sha mismatch 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "pr-edited", } `; @@ -150,6 +195,7 @@ Object { exports[`workers/branch/index processBranch skips branch if branch edited and no PR found 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "pr-edited", } `; @@ -157,6 +203,7 @@ Object { exports[`workers/branch/index processBranch skips branch if edited PR found 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "pr-edited", } `; @@ -164,6 +211,7 @@ Object { exports[`workers/branch/index processBranch skips branch if not scheduled and branch does not exist 1`] = ` Object { "branchExists": false, + "prNo": undefined, "result": "not-scheduled", } `; @@ -171,13 +219,15 @@ Object { exports[`workers/branch/index processBranch skips branch if not scheduled and not updating out of schedule 1`] = ` Object { "branchExists": true, - "result": "not-scheduled", + "prNo": undefined, + "result": "update-not-scheduled", } `; exports[`workers/branch/index processBranch skips branch if not stabilityDays not met 1`] = ` Object { "branchExists": false, + "prNo": undefined, "result": "pending", } `; @@ -185,6 +235,7 @@ Object { exports[`workers/branch/index processBranch skips branch if target branch changed 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "pr-edited", } `; @@ -192,6 +243,7 @@ Object { exports[`workers/branch/index processBranch swallows branch errors 1`] = ` Object { "branchExists": false, + "prNo": undefined, "result": "error", } `; @@ -199,6 +251,7 @@ Object { exports[`workers/branch/index processBranch swallows pr errors 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "done", } `; @@ -206,6 +259,7 @@ Object { exports[`workers/branch/index processBranch throws and swallows branch errors 1`] = ` Object { "branchExists": true, + "prNo": undefined, "result": "pr-created", } `; diff --git a/lib/workers/branch/index.spec.ts b/lib/workers/branch/index.spec.ts index dc348e821410ec..418d9fdc90ee97 100644 --- a/lib/workers/branch/index.spec.ts +++ b/lib/workers/branch/index.spec.ts @@ -24,7 +24,7 @@ import type { EnsurePrResult } from '../pr'; import * as _prAutomerge from '../pr/automerge'; import type { Pr } from '../repository/onboarding/branch/check'; import type { BranchConfig, BranchUpgradeConfig } from '../types'; -import { PrResult } from '../types'; +import { BranchResult } from '../types'; import * as _automerge from './automerge'; import * as _checkExisting from './check-existing'; import * as _commit from './commit'; @@ -89,7 +89,6 @@ describe(getName(), () => { platform.massageMarkdown.mockImplementation((prBody) => prBody); prWorker.ensurePr.mockResolvedValue({ - prResult: PrResult.Created, pr: { title: '', sourceBranch: '', @@ -297,7 +296,7 @@ describe(getName(), () => { }); git.branchExists.mockReturnValue(true); prWorker.ensurePr.mockResolvedValueOnce({ - prResult: PrResult.LimitReached, + prBlockedBy: 'RateLimited', }); limits.isLimitReached.mockReturnValue(false); expect(await branchWorker.processBranch(config)).toMatchSnapshot(); @@ -336,7 +335,7 @@ describe(getName(), () => { artifactErrors: [], updatedArtifacts: [], }); - config.pendingChecks = ['stabilityDays']; + config.pendingChecks = true; expect(await branchWorker.processBranch(config)).toMatchSnapshot(); }); @@ -403,7 +402,7 @@ describe(getName(), () => { commit.commitFilesToBranch.mockResolvedValueOnce(null); automerge.tryBranchAutomerge.mockResolvedValueOnce('failed'); prWorker.ensurePr.mockResolvedValueOnce({ - prResult: PrResult.AwaitingApproval, + prBlockedBy: 'NeedsApproval', }); expect(await branchWorker.processBranch(config)).toMatchSnapshot(); }); @@ -420,7 +419,58 @@ describe(getName(), () => { commit.commitFilesToBranch.mockResolvedValueOnce(null); automerge.tryBranchAutomerge.mockResolvedValueOnce('failed'); prWorker.ensurePr.mockResolvedValueOnce({ - prResult: PrResult.AwaitingNotPending, + prBlockedBy: 'AwaitingTests', + }); + expect(await branchWorker.processBranch(config)).toMatchSnapshot(); + }); + it('returns if branch automerge is pending', async () => { + expect.assertions(1); + getUpdated.getUpdatedPackageFiles.mockResolvedValueOnce({ + updatedPackageFiles: [{}], + } as PackageFilesResult); + npmPostExtract.getAdditionalFiles.mockResolvedValueOnce({ + artifactErrors: [], + updatedArtifacts: [{}], + } as WriteExistingFilesResult); + git.branchExists.mockReturnValue(true); + commit.commitFilesToBranch.mockResolvedValueOnce(null); + automerge.tryBranchAutomerge.mockResolvedValueOnce('no automerge'); + prWorker.ensurePr.mockResolvedValueOnce({ + prBlockedBy: 'BranchAutomerge', + }); + expect(await branchWorker.processBranch(config)).toMatchSnapshot(); + }); + it('returns if PR creation failed', async () => { + expect.assertions(1); + getUpdated.getUpdatedPackageFiles.mockResolvedValueOnce({ + updatedPackageFiles: [{}], + } as PackageFilesResult); + npmPostExtract.getAdditionalFiles.mockResolvedValueOnce({ + artifactErrors: [], + updatedArtifacts: [{}], + } as WriteExistingFilesResult); + git.branchExists.mockReturnValue(true); + commit.commitFilesToBranch.mockResolvedValueOnce(null); + automerge.tryBranchAutomerge.mockResolvedValueOnce('failed'); + prWorker.ensurePr.mockResolvedValueOnce({ + prBlockedBy: 'Error', + }); + expect(await branchWorker.processBranch(config)).toMatchSnapshot(); + }); + it('handles unknown PrBlockedBy', async () => { + expect.assertions(1); + getUpdated.getUpdatedPackageFiles.mockResolvedValueOnce({ + updatedPackageFiles: [{}], + } as PackageFilesResult); + npmPostExtract.getAdditionalFiles.mockResolvedValueOnce({ + artifactErrors: [], + updatedArtifacts: [{}], + } as WriteExistingFilesResult); + git.branchExists.mockReturnValue(true); + commit.commitFilesToBranch.mockResolvedValueOnce(null); + automerge.tryBranchAutomerge.mockResolvedValueOnce('failed'); + prWorker.ensurePr.mockResolvedValueOnce({ + prBlockedBy: 'whoops' as any, }); expect(await branchWorker.processBranch(config)).toMatchSnapshot(); }); @@ -455,7 +505,6 @@ describe(getName(), () => { git.branchExists.mockReturnValue(true); automerge.tryBranchAutomerge.mockResolvedValueOnce('failed'); prWorker.ensurePr.mockResolvedValueOnce({ - prResult: PrResult.Created, pr: {}, } as EnsurePrResult); prAutomerge.checkAutoMerge.mockResolvedValueOnce({ automerged: true }); @@ -476,7 +525,6 @@ describe(getName(), () => { git.branchExists.mockReturnValue(true); automerge.tryBranchAutomerge.mockResolvedValueOnce('stale'); prWorker.ensurePr.mockResolvedValueOnce({ - prResult: PrResult.Created, pr: {}, } as EnsurePrResult); prAutomerge.checkAutoMerge.mockResolvedValueOnce({ automerged: false }); @@ -501,7 +549,6 @@ describe(getName(), () => { git.branchExists.mockReturnValue(true); automerge.tryBranchAutomerge.mockResolvedValueOnce('failed'); prWorker.ensurePr.mockResolvedValueOnce({ - prResult: PrResult.Created, pr: {}, } as EnsurePrResult); prAutomerge.checkAutoMerge.mockResolvedValueOnce({ automerged: true }); @@ -522,7 +569,6 @@ describe(getName(), () => { git.branchExists.mockReturnValue(true); automerge.tryBranchAutomerge.mockResolvedValueOnce('failed'); prWorker.ensurePr.mockResolvedValueOnce({ - prResult: PrResult.Created, pr: {}, } as EnsurePrResult); prAutomerge.checkAutoMerge.mockResolvedValueOnce({ automerged: true }); @@ -544,7 +590,6 @@ describe(getName(), () => { git.branchExists.mockReturnValue(true); automerge.tryBranchAutomerge.mockResolvedValueOnce('failed'); prWorker.ensurePr.mockResolvedValueOnce({ - prResult: PrResult.Created, pr: {}, } as EnsurePrResult); prAutomerge.checkAutoMerge.mockResolvedValueOnce({ automerged: true }); @@ -566,7 +611,6 @@ describe(getName(), () => { git.branchExists.mockReturnValue(false); automerge.tryBranchAutomerge.mockResolvedValueOnce('failed'); prWorker.ensurePr.mockResolvedValueOnce({ - prResult: PrResult.Created, pr: {}, } as EnsurePrResult); prAutomerge.checkAutoMerge.mockResolvedValueOnce({ automerged: true }); @@ -587,7 +631,6 @@ describe(getName(), () => { git.branchExists.mockReturnValue(true); automerge.tryBranchAutomerge.mockResolvedValueOnce('failed'); prWorker.ensurePr.mockResolvedValueOnce({ - prResult: PrResult.Created, pr: {}, } as EnsurePrResult); prAutomerge.checkAutoMerge.mockResolvedValueOnce({ automerged: true }); @@ -698,7 +741,6 @@ describe(getName(), () => { git.isBranchModified.mockResolvedValueOnce(true); schedule.isScheduledNow.mockReturnValueOnce(false); prWorker.ensurePr.mockResolvedValueOnce({ - prResult: PrResult.Created, pr: {}, } as EnsurePrResult); commit.commitFilesToBranch.mockResolvedValueOnce(null); @@ -805,9 +847,6 @@ describe(getName(), () => { }); expect(result).toMatchSnapshot(); - expect(exec.exec).toHaveBeenCalledWith('echo semver', { - cwd: '/localDir', - }); const errorMessage = expect.stringContaining( "Post-upgrade command 'disallowed task' does not match allowed pattern '^echo {{{versioning}}}$'" ); @@ -1205,5 +1244,20 @@ describe(getName(), () => { ).toString() ).toBe('modified file content'); }); + it('returns when rebaseWhen=never', async () => { + getUpdated.getUpdatedPackageFiles.mockResolvedValueOnce({ + ...updatedPackageFiles, + }); + npmPostExtract.getAdditionalFiles.mockResolvedValueOnce({ + artifactErrors: [], + updatedArtifacts: [], + }); + git.branchExists.mockReturnValue(true); + commit.commitFilesToBranch.mockResolvedValueOnce(null); + expect( + await branchWorker.processBranch({ ...config, rebaseWhen: 'never' }) + ).toMatchObject({ result: BranchResult.NoWork }); + expect(commit.commitFilesToBranch).not.toHaveBeenCalled(); + }); }); }); diff --git a/lib/workers/branch/index.ts b/lib/workers/branch/index.ts index 36b8199c2f7157..393fdce2379db4 100644 --- a/lib/workers/branch/index.ts +++ b/lib/workers/branch/index.ts @@ -30,7 +30,7 @@ import { import { Limit, isLimitReached } from '../global/limits'; import { ensurePr, getPlatformPrOptions } from '../pr'; import { checkAutoMerge } from '../pr/automerge'; -import { BranchConfig, BranchResult, PrResult } from '../types'; +import { BranchConfig, BranchResult, PrBlockedBy } from '../types'; import { tryBranchAutomerge } from './automerge'; import { prAlreadyExisted } from './check-existing'; import { commitFilesToBranch } from './commit'; @@ -63,6 +63,8 @@ async function deleteBranchSilently(branchName: string): Promise { export interface ProcessBranchResult { branchExists: boolean; + prBlockedBy?: PrBlockedBy; + prNo?: number; result: BranchResult; } @@ -73,7 +75,7 @@ export async function processBranch( logger.trace({ config }, 'processBranch()'); await checkoutBranch(config.baseBranch); const branchExists = gitBranchExists(config.branchName); - const branchPr = await platform.getBranchPr(config.branchName); + let branchPr = await platform.getBranchPr(config.branchName); logger.debug(`branchExists=${branchExists}`); const dependencyDashboardCheck = config.dependencyDashboardChecks?.[config.branchName]; @@ -92,7 +94,11 @@ export async function processBranch( 'Closed PR already exists. Skipping branch.' ); await handlepr(config, existingPr); - return { branchExists: false, result: BranchResult.AlreadyExisted }; + return { + branchExists: false, + prNo: existingPr.number, + result: BranchResult.AlreadyExisted, + }; } // istanbul ignore if if (!branchExists && config.dependencyDashboardApproval) { @@ -100,7 +106,11 @@ export async function processBranch( logger.debug(`Branch ${config.branchName} is approved for creation`); } else { logger.debug(`Branch ${config.branchName} needs approval`); - return { branchExists, result: BranchResult.NeedsApproval }; + return { + branchExists, + prNo: branchPr?.number, + result: BranchResult.NeedsApproval, + }; } } if ( @@ -110,7 +120,11 @@ export async function processBranch( !config.isVulnerabilityAlert ) { logger.debug('Reached branch limit - skipping branch creation'); - return { branchExists, result: BranchResult.BranchLimitReached }; + return { + branchExists, + prNo: branchPr?.number, + result: BranchResult.BranchLimitReached, + }; } if ( isLimitReached(Limit.Commits) && @@ -118,14 +132,22 @@ export async function processBranch( !config.isVulnerabilityAlert ) { logger.debug('Reached commits limit - skipping branch'); - return { branchExists, result: BranchResult.CommitLimitReached }; + return { + branchExists, + prNo: branchPr?.number, + result: BranchResult.CommitLimitReached, + }; } if ( !branchExists && branchConfig.pendingChecks && !dependencyDashboardCheck ) { - return { branchExists: false, result: BranchResult.Pending }; + return { + branchExists: false, + prNo: branchPr?.number, + result: BranchResult.Pending, + }; } if (branchExists) { logger.debug('Checking if PR has been edited'); @@ -162,7 +184,11 @@ export async function processBranch( platformOptions: getPlatformPrOptions(config), }); } - return { branchExists, result: BranchResult.PrEdited }; + return { + branchExists, + prNo: branchPr.number, + result: BranchResult.PrEdited, + }; } } } else if (branchIsModified) { @@ -172,7 +198,11 @@ export async function processBranch( }); if (!oldPr) { logger.debug('Branch has been edited but found no PR - skipping'); - return { branchExists, result: BranchResult.PrEdited }; + return { + branchExists, + prNo: branchPr?.number, + result: BranchResult.PrEdited, + }; } const branchSha = getBranchCommit(config.branchName); const oldPrSha = oldPr?.sha; @@ -186,7 +216,11 @@ export async function processBranch( { oldPrNumber: oldPr.number, oldPrSha, branchSha }, 'Found old PR but the SHA is different' ); - return { branchExists, result: BranchResult.PrEdited }; + return { + branchExists, + prNo: branchPr?.number, + result: BranchResult.PrEdited, + }; } } } @@ -196,16 +230,28 @@ export async function processBranch( if (!config.isScheduledNow && !dependencyDashboardCheck) { if (!branchExists) { logger.debug('Skipping branch creation as not within schedule'); - return { branchExists, result: BranchResult.NotScheduled }; + return { + branchExists, + prNo: branchPr?.number, + result: BranchResult.NotScheduled, + }; } if (config.updateNotScheduled === false && !config.rebaseRequested) { logger.debug('Skipping branch update as not within schedule'); - return { branchExists, result: BranchResult.NotScheduled }; + return { + branchExists, + prNo: branchPr?.number, + result: BranchResult.UpdateNotScheduled, + }; } // istanbul ignore if if (!branchPr) { logger.debug('Skipping PR creation out of schedule'); - return { branchExists, result: BranchResult.NotScheduled }; + return { + branchExists, + prNo: branchPr?.number, + result: BranchResult.NotScheduled, + }; } logger.debug( 'Branch + PR exists but is not scheduled -- will update if necessary' @@ -245,7 +291,11 @@ export async function processBranch( ['not-pending', 'status-success'].includes(config.prCreation) ) { logger.debug('Skipping branch creation due to stability days not met'); - return { branchExists, result: BranchResult.Pending }; + return { + branchExists, + prNo: branchPr?.number, + result: BranchResult.Pending, + }; } } @@ -341,17 +391,27 @@ export async function processBranch( }); } } - config.forceCommit = - !!dependencyDashboardCheck || - config.rebaseRequested || - branchPr?.isConflicted; + const forcedManually = !!dependencyDashboardCheck || config.rebaseRequested; + if (!forcedManually && config.rebaseWhen === 'never') { + logger.debug(`Skipping commit (rebaseWhen=never)`); + return { + branchExists, + prNo: branchPr?.number, + result: BranchResult.NoWork, + }; + } + config.forceCommit = forcedManually || branchPr?.isConflicted; const commitSha = await commitFilesToBranch(config); // istanbul ignore if if (branchPr && platform.refreshPr) { await platform.refreshPr(branchPr.number); } if (!commitSha && !branchExists) { - return { branchExists, result: BranchResult.NoWork }; + return { + branchExists, + prNo: branchPr?.number, + result: BranchResult.NoWork, + }; } if (commitSha) { const action = branchExists ? 'updated' : 'created'; @@ -368,7 +428,11 @@ export async function processBranch( (config.requiredStatusChecks?.length || config.prCreation !== 'immediate') ) { logger.debug({ commitSha }, `Branch status pending`); - return { branchExists: true, result: BranchResult.Pending }; + return { + branchExists: true, + prNo: branchPr?.number, + result: BranchResult.Pending, + }; } // Try to automerge branch and finish if successful, but only if branch already existed before this run @@ -456,7 +520,11 @@ export async function processBranch( logger.warn('Error updating branch: update failure'); } else if (err.message.startsWith('bundler-')) { // we have already warned inside the bundler artifacts error handling, so just return - return { branchExists: true, result: BranchResult.Error }; + return { + branchExists: true, + prNo: branchPr?.number, + result: BranchResult.Error, + }; } else if ( err.messagee && err.message.includes('fatal: Authentication failed') @@ -475,27 +543,47 @@ export async function processBranch( logger.warn({ err }, `Error updating branch`); } // Don't throw here - we don't want to stop the other renovations - return { branchExists, result: BranchResult.Error }; + return { branchExists, prNo: branchPr?.number, result: BranchResult.Error }; } try { logger.debug('Ensuring PR'); logger.debug( `There are ${config.errors.length} errors and ${config.warnings.length} warnings` ); - const { prResult: result, pr } = await ensurePr(config); - if (result === PrResult.LimitReached && !config.isVulnerabilityAlert) { - logger.debug('Reached PR limit - skipping PR creation'); - return { branchExists, result: BranchResult.PrLimitReached }; - } - // TODO: ensurePr should check for automerge itself (#9719) - if (result === PrResult.AwaitingApproval) { - return { branchExists, result: BranchResult.NeedsPrApproval }; - } - if ( - result === PrResult.AwaitingGreenBranch || - result === PrResult.AwaitingNotPending - ) { - return { branchExists, result: BranchResult.Pending }; + const { prBlockedBy, pr } = await ensurePr(config); + branchPr = pr; + if (prBlockedBy) { + if (prBlockedBy === 'RateLimited' && !config.isVulnerabilityAlert) { + logger.debug('Reached PR limit - skipping PR creation'); + return { + branchExists, + prBlockedBy, + result: BranchResult.PrLimitReached, + }; + } + // TODO: ensurePr should check for automerge itself (#9719) + if (prBlockedBy === 'NeedsApproval') { + return { + branchExists, + prBlockedBy, + result: BranchResult.NeedsPrApproval, + }; + } + if (prBlockedBy === 'AwaitingTests') { + return { branchExists, prBlockedBy, result: BranchResult.Pending }; + } + if (prBlockedBy === 'BranchAutomerge') { + return { + branchExists, + prBlockedBy, + result: BranchResult.Done, + }; + } + if (prBlockedBy === 'Error') { + return { branchExists, prBlockedBy, result: BranchResult.Error }; + } + logger.warn({ prBlockedBy }, 'Unknown PrBlockedBy result'); + return { branchExists, prBlockedBy, result: BranchResult.Error }; } if (pr) { if (config.artifactErrors?.length) { @@ -587,7 +675,11 @@ export async function processBranch( logger.error({ err }, `Error ensuring PR: ${String(err.message)}`); } if (!branchExists) { - return { branchExists: true, result: BranchResult.PrCreated }; + return { + branchExists: true, + prNo: branchPr?.number, + result: BranchResult.PrCreated, + }; } - return { branchExists, result: BranchResult.Done }; + return { branchExists, prNo: branchPr?.number, result: BranchResult.Done }; } diff --git a/lib/workers/global/autodiscover.ts b/lib/workers/global/autodiscover.ts index bcb993d8dd6f0c..03b66b293acdf0 100644 --- a/lib/workers/global/autodiscover.ts +++ b/lib/workers/global/autodiscover.ts @@ -1,6 +1,6 @@ import is from '@sindresorhus/is'; import minimatch from 'minimatch'; -import type { GlobalConfig } from '../../config/types'; +import type { AllConfig } from '../../config/types'; import { logger } from '../../logger'; import { platform } from '../../platform'; @@ -10,8 +10,8 @@ function repoName(value: string | { repository: string }): string { } export async function autodiscoverRepositories( - config: GlobalConfig -): Promise { + config: AllConfig +): Promise { if (!config.autodiscover) { if (!config.repositories?.length) { logger.warn( diff --git a/lib/config/config/__fixtures__/argv.ts b/lib/workers/global/config/parse/__fixtures__/argv.ts similarity index 100% rename from lib/config/config/__fixtures__/argv.ts rename to lib/workers/global/config/parse/__fixtures__/argv.ts diff --git a/lib/config/config/__fixtures__/file.js b/lib/workers/global/config/parse/__fixtures__/file.js similarity index 100% rename from lib/config/config/__fixtures__/file.js rename to lib/workers/global/config/parse/__fixtures__/file.js diff --git a/lib/config/config/__fixtures__/file2.js b/lib/workers/global/config/parse/__fixtures__/file2.js similarity index 100% rename from lib/config/config/__fixtures__/file2.js rename to lib/workers/global/config/parse/__fixtures__/file2.js diff --git a/lib/workers/global/config/parse/__fixtures__/private.pem b/lib/workers/global/config/parse/__fixtures__/private.pem new file mode 100644 index 00000000000000..efee57e904c99d --- /dev/null +++ b/lib/workers/global/config/parse/__fixtures__/private.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAzVLc1KmhWxcLnoPTPpQwxVHySFx4vY+0Sk+2AdnlJvrTFlNR +8XgBPdgU9SDwHFFTYyXRQ/msm0YMOBRoIdz4/psz6IzRV80aOtDIkUPAU+cdqnje +3rusrPnqCykDuIHGoBZ8zlt57t/8OVNOduUflrQRqMFors8iLWcNjuGJfZAusI5B +M8KYYDPL2oUo/8AV0bWN8MdNnxDSwGtan4bbzrtG6dpNzyntsG5DkL1h8OZRki4S +kHf+71ZzJFz1KcyrzBVJ9DBxdiJdmLLkv9caLLc2QS0VFmtfLnlrga3Sahy6YNwc +rN33aaNOXwFPSErsKWIgIf16eOaSUQCFusXY9QIDAQABAoIBAQCkltMM6nm9Ikkf +JX9V/8bkth7o4K+tDSAyHZnB/CBUUeaaU+oxDci5AZkzMtcnbA3TQcJxohg6VDmB +TuJ2msNCnblLpm492t023pyYzd3DpFXEjKXjmEAAXUm+7n7cDbPiKoSbivrAgO6Y +KW6RonPjA6/QPlIjJ0m3aY+VxLfJXTYVfFBk+0HGEAvmrSSSEezXej8Qzs9CRKtz +cQkR5Bs749SS509MHxaslP7n366EvJBJkqrjUxA6kbxOxUkuuOWF9jfduDAG+NYc +pe4IXFOYMpK/w29wqvkNulKYs+FHXr1sGjkztGjyNnjP0NnX+r0EYU07xLJ26krD +KMWQG8sBAoGBAO93iFrPnEBnnKojI8b+u3YowVCz0HVuHHIStHd9uUgfsn/Bxfls +HRIlg8l1MJd69TT0knOhkJxBCP+b/qBvid7YLbTxIVELCAfLAzsmGgY+DEN/TTUb +FDvHGa/drCnkSR/O/RbtHvMQISzTja1siYrdwwY+wpwzR2tB7ZH/9iO1AoGBANt/ +3Srpv4BbP7JZ+cNte4JfI2gZq41mo+DF5ryuFZzsP3R5SZ1BOapQGyLEATQaoxsJ +QZsI73KPBzab0/+E75qJuXckIGmFftHXgoQpClGvPaEDF9M21QW5Y3vKVa/45qhy +3wpb8gEYqrt1x0rmzyumCtCD8J470Er64gE1eKhBAoGBALW6ScFYwqRhvROkvTb8 +A7mE7kfXXgBwAqhTJ59yytRAMc8gd6R0do9Z5uxQwgKDLmj0ndugpcTe2fxZHuAU +JVX3SqCBSZ5eN8bqOtZ9cMyB8/6ZMjd2CGHhE85R9KCJ/TBlfc4TPySIfhStq1wL +/UlkR+eKY1f01mNAUhE1ZU7tAoGBAKwS/B51KsSERFYcRToYbRfSX55vaVarnVNL +scw+qQDhEAnOP5CBHqTOscc6YzsmmrFKO10/zv8+80ezN6n73B6JU5T8BFDU74uv +6EiVJ9rLh4PfOeFB/hPDtyLHhw8yEBkEHKgxVnHXlZjqBzdH5Cdyvs2icZKKj4sI +TP7nnVRBAoGAPZBjjTn9HgB9et5Kdovp+7nr842WboxwklVyTpbNGAjIxNXIXex+ +TST21UbquTIpIrpYRk1WMIu9T5PndAagDWVEsmUYQZeuhmuY64K+iviRsfdGthws +nbbR8sMEkn4XEtPZfrEBq27g01RNmIIMW5Es2O5N2AjLlyeBOh099Fw= +-----END RSA PRIVATE KEY----- diff --git a/lib/config/config/__fixtures__/with-force.js b/lib/workers/global/config/parse/__fixtures__/with-force.js similarity index 100% rename from lib/config/config/__fixtures__/with-force.js rename to lib/workers/global/config/parse/__fixtures__/with-force.js diff --git a/lib/config/__snapshots__/env.spec.ts.snap b/lib/workers/global/config/parse/__snapshots__/env.spec.ts.snap similarity index 59% rename from lib/config/__snapshots__/env.spec.ts.snap rename to lib/workers/global/config/parse/__snapshots__/env.spec.ts.snap index 98f9c387eb70ce..840b3afa04f6dd 100644 --- a/lib/config/__snapshots__/env.spec.ts.snap +++ b/lib/workers/global/config/parse/__snapshots__/env.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`config/env .getConfig(env) supports Azure DevOps 1`] = ` +exports[`workers/global/config/parse/env .getConfig(env) supports Azure DevOps 1`] = ` Object { "endpoint": "an Azure DevOps endpoint", "hostRules": Array [], @@ -9,7 +9,7 @@ Object { } `; -exports[`config/env .getConfig(env) supports Bitbucket token 1`] = ` +exports[`workers/global/config/parse/env .getConfig(env) supports Bitbucket token 1`] = ` Object { "endpoint": "a bitbucket endpoint", "hostRules": Array [], @@ -19,7 +19,7 @@ Object { } `; -exports[`config/env .getConfig(env) supports Bitbucket username/password 1`] = ` +exports[`workers/global/config/parse/env .getConfig(env) supports Bitbucket username/password 1`] = ` Object { "endpoint": "a bitbucket endpoint", "hostRules": Array [], @@ -29,14 +29,14 @@ Object { } `; -exports[`config/env .getConfig(env) supports GitHub custom endpoint 1`] = ` +exports[`workers/global/config/parse/env .getConfig(env) supports GitHub custom endpoint 1`] = ` Object { "endpoint": "a ghe endpoint", "hostRules": Array [], } `; -exports[`config/env .getConfig(env) supports GitHub custom endpoint and github.com 1`] = ` +exports[`workers/global/config/parse/env .getConfig(env) supports GitHub custom endpoint and github.com 1`] = ` Object { "endpoint": "a ghe endpoint", "hostRules": Array [ @@ -50,7 +50,7 @@ Object { } `; -exports[`config/env .getConfig(env) supports GitHub custom endpoint and gitlab.com 1`] = ` +exports[`workers/global/config/parse/env .getConfig(env) supports GitHub custom endpoint and gitlab.com 1`] = ` Object { "endpoint": "a ghe endpoint", "hostRules": Array [], @@ -58,14 +58,14 @@ Object { } `; -exports[`config/env .getConfig(env) supports GitHub token 1`] = ` +exports[`workers/global/config/parse/env .getConfig(env) supports GitHub token 1`] = ` Object { "hostRules": Array [], "token": "github.com token", } `; -exports[`config/env .getConfig(env) supports GitLab custom endpoint 1`] = ` +exports[`workers/global/config/parse/env .getConfig(env) supports GitLab custom endpoint 1`] = ` Object { "endpoint": "a gitlab endpoint", "hostRules": Array [], @@ -74,7 +74,7 @@ Object { } `; -exports[`config/env .getConfig(env) supports GitLab token 1`] = ` +exports[`workers/global/config/parse/env .getConfig(env) supports GitLab token 1`] = ` Object { "hostRules": Array [], "platform": "gitlab", @@ -82,7 +82,7 @@ Object { } `; -exports[`config/env .getConfig(env) supports datasource env token 1`] = ` +exports[`workers/global/config/parse/env .getConfig(env) supports datasource env token 1`] = ` Object { "hostRules": Array [ Object { @@ -93,7 +93,7 @@ Object { } `; -exports[`config/env .getConfig(env) supports docker username/password 1`] = ` +exports[`workers/global/config/parse/env .getConfig(env) supports docker username/password 1`] = ` Object { "hostRules": Array [ Object { @@ -105,7 +105,7 @@ Object { } `; -exports[`config/env .getConfig(env) supports domain and host names with case insensitivity 1`] = ` +exports[`workers/global/config/parse/env .getConfig(env) supports domain and host names with case insensitivity 1`] = ` Object { "hostRules": Array [ Object { @@ -122,7 +122,7 @@ Object { } `; -exports[`config/env .getConfig(env) supports password-only 1`] = ` +exports[`workers/global/config/parse/env .getConfig(env) supports password-only 1`] = ` Object { "hostRules": Array [ Object { diff --git a/lib/config/__snapshots__/file.spec.ts.snap b/lib/workers/global/config/parse/__snapshots__/file.spec.ts.snap similarity index 54% rename from lib/config/__snapshots__/file.spec.ts.snap rename to lib/workers/global/config/parse/__snapshots__/file.spec.ts.snap index 2050852983026d..b80b18a014aa53 100644 --- a/lib/config/__snapshots__/file.spec.ts.snap +++ b/lib/workers/global/config/parse/__snapshots__/file.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`config/file .getConfig() migrates 1`] = ` +exports[`workers/global/config/parse/file .getConfig() migrates 1`] = ` Object { "rangeStrategy": "bump", } diff --git a/lib/config/cli.spec.ts b/lib/workers/global/config/parse/cli.spec.ts similarity index 95% rename from lib/config/cli.spec.ts rename to lib/workers/global/config/parse/cli.spec.ts index b9e630c2c46f2d..b0b86fd2de14e1 100644 --- a/lib/config/cli.spec.ts +++ b/lib/workers/global/config/parse/cli.spec.ts @@ -1,8 +1,8 @@ -import { getName } from '../../test/util'; -import * as datasourceDocker from '../datasource/docker'; +import { getName } from '../../../../../test/util'; +import type { RenovateOptions } from '../../../../config/types'; +import * as datasourceDocker from '../../../../datasource/docker'; +import getArgv from './__fixtures__/argv'; import * as cli from './cli'; -import getArgv from './config/__fixtures__/argv'; -import type { RenovateOptions } from './types'; describe(getName(), () => { let argv: string[]; diff --git a/lib/config/cli.ts b/lib/workers/global/config/parse/cli.ts similarity index 88% rename from lib/config/cli.ts rename to lib/workers/global/config/parse/cli.ts index ec0084a67d4551..6cae6955447752 100644 --- a/lib/config/cli.ts +++ b/lib/workers/global/config/parse/cli.ts @@ -1,7 +1,7 @@ import { Command } from 'commander'; -import { version } from '../../package.json'; -import { getOptions } from './definitions'; -import type { GlobalConfig, RenovateCliConfig, RenovateOptions } from './types'; +import { version } from '../../../../../package.json'; +import { getOptions } from '../../../../config/definitions'; +import type { AllConfig, RenovateOptions } from '../../../../config/types'; export function getCliName(option: Partial): string { if (option.cli === false) { @@ -11,7 +11,7 @@ export function getCliName(option: Partial): string { return `--${nameWithHyphens.toLowerCase()}`; } -export function getConfig(input: string[]): GlobalConfig { +export function getConfig(input: string[]): AllConfig { // massage migrated configuration keys const argv = input .map((a) => @@ -27,7 +27,7 @@ export function getConfig(input: string[]): GlobalConfig { .filter((a) => !a.startsWith('--git-fs')); const options = getOptions(); - const config: RenovateCliConfig = {}; + const config: Record = {}; const coersions: Record unknown> = { boolean: (val: string): boolean => { @@ -88,7 +88,7 @@ export function getConfig(input: string[]): GlobalConfig { console.log(''); console.log(' $ renovate --token abc123 singapore/lint-condo'); console.log( - ' $ renovate --labels=renovate,dependency --ignore-unstable=false --log-level debug singapore/lint-condo' + ' $ LOG_LEVEL=debug renovate --labels=renovate,dependency --ignore-unstable=false singapore/lint-condo' ); console.log(' $ renovate singapore/lint-condo singapore/package-test'); console.log( diff --git a/lib/config/env.spec.ts b/lib/workers/global/config/parse/env.spec.ts similarity index 97% rename from lib/config/env.spec.ts rename to lib/workers/global/config/parse/env.spec.ts index 6b9915f9e981f4..4aa0158a87e62a 100644 --- a/lib/config/env.spec.ts +++ b/lib/workers/global/config/parse/env.spec.ts @@ -1,10 +1,10 @@ -import { getName } from '../../test/util'; +import { getName } from '../../../../../test/util'; +import type { RenovateOptions } from '../../../../config/types'; import { PLATFORM_TYPE_BITBUCKET, PLATFORM_TYPE_GITLAB, -} from '../constants/platforms'; +} from '../../../../constants/platforms'; import * as env from './env'; -import type { RenovateOptions } from './types'; describe(getName(), () => { describe('.getConfig(env)', () => { diff --git a/lib/config/env.ts b/lib/workers/global/config/parse/env.ts similarity index 90% rename from lib/config/env.ts rename to lib/workers/global/config/parse/env.ts index 9689b70353998a..3fa43334fb8a14 100644 --- a/lib/config/env.ts +++ b/lib/workers/global/config/parse/env.ts @@ -1,11 +1,11 @@ import is from '@sindresorhus/is'; -import { PLATFORM_TYPE_GITHUB } from '../constants/platforms'; -import { getDatasourceList } from '../datasource'; -import { logger } from '../logger'; -import type { HostRule } from '../types'; -import { getOptions } from './definitions'; -import type { GlobalConfig, RenovateOptions } from './types'; +import { getOptions } from '../../../../config/definitions'; +import type { AllConfig, RenovateOptions } from '../../../../config/types'; +import { PLATFORM_TYPE_GITHUB } from '../../../../constants/platforms'; +import { getDatasourceList } from '../../../../datasource'; +import { logger } from '../../../../logger'; +import type { HostRule } from '../../../../types'; // istanbul ignore if if (process.env.ENV_PREFIX) { @@ -27,10 +27,10 @@ export function getEnvName(option: Partial): string { return `RENOVATE_${nameWithUnderscores.toUpperCase()}`; } -export function getConfig(env: NodeJS.ProcessEnv): GlobalConfig { +export function getConfig(env: NodeJS.ProcessEnv): AllConfig { const options = getOptions(); - let config: GlobalConfig = {}; + let config: AllConfig = {}; if (env.RENOVATE_CONFIG) { try { diff --git a/lib/config/file.spec.ts b/lib/workers/global/config/parse/file.spec.ts similarity index 85% rename from lib/config/file.spec.ts rename to lib/workers/global/config/parse/file.spec.ts index eaa768a8047bc1..df3540a416ac03 100644 --- a/lib/config/file.spec.ts +++ b/lib/workers/global/config/parse/file.spec.ts @@ -1,8 +1,8 @@ import fs from 'fs'; import { DirectoryResult, dir } from 'tmp-promise'; import upath from 'upath'; -import { getName } from '../../test/util'; -import customConfig from './config/__fixtures__/file'; +import { getName } from '../../../../../test/util'; +import customConfig from './__fixtures__/file'; import * as file from './file'; describe(getName(), () => { @@ -23,19 +23,13 @@ describe(getName(), () => { ); }); it('parses custom config file', () => { - const configFile = upath.resolve( - __dirname, - './config/__fixtures__/file.js' - ); + const configFile = upath.resolve(__dirname, './__fixtures__/file.js'); expect(file.getConfig({ RENOVATE_CONFIG_FILE: configFile })).toEqual( customConfig ); }); it('migrates', () => { - const configFile = upath.resolve( - __dirname, - './config/__fixtures__/file2.js' - ); + const configFile = upath.resolve(__dirname, './__fixtures__/file2.js'); const res = file.getConfig({ RENOVATE_CONFIG_FILE: configFile }); expect(res).toMatchSnapshot(); expect(res.rangeStrategy).toEqual('bump'); diff --git a/lib/config/file.ts b/lib/workers/global/config/parse/file.ts similarity index 77% rename from lib/config/file.ts rename to lib/workers/global/config/parse/file.ts index f6b59fce0f7668..c43544d8685433 100644 --- a/lib/config/file.ts +++ b/lib/workers/global/config/parse/file.ts @@ -1,15 +1,15 @@ import upath from 'upath'; -import { logger } from '../logger'; -import { migrateConfig } from './migration'; -import type { GlobalConfig } from './types'; +import { migrateConfig } from '../../../../config/migration'; +import type { AllConfig } from '../../../../config/types'; +import { logger } from '../../../../logger'; -export function getConfig(env: NodeJS.ProcessEnv): GlobalConfig { +export function getConfig(env: NodeJS.ProcessEnv): AllConfig { let configFile = env.RENOVATE_CONFIG_FILE || 'config'; if (!upath.isAbsolute(configFile)) { configFile = `${process.cwd()}/${configFile}`; logger.debug('Checking for config file in ' + configFile); } - let config: GlobalConfig = {}; + let config: AllConfig = {}; try { // eslint-disable-next-line global-require,import/no-dynamic-require config = require(configFile); diff --git a/lib/workers/global/config/parse/index.spec.ts b/lib/workers/global/config/parse/index.spec.ts new file mode 100644 index 00000000000000..20412bddc1cc5f --- /dev/null +++ b/lib/workers/global/config/parse/index.spec.ts @@ -0,0 +1,113 @@ +import upath from 'upath'; +import { getName } from '../../../../../test/util'; +import { readFile } from '../../../../util/fs'; +import getArgv from './__fixtures__/argv'; + +jest.mock('../../../../datasource/npm'); +try { + jest.mock('../../config.js'); +} catch (err) { + // file does not exist +} + +describe(getName(), () => { + describe('.parseConfigs(env, defaultArgv)', () => { + let configParser: typeof import('.'); + let defaultArgv: string[]; + let defaultEnv: NodeJS.ProcessEnv; + beforeEach(async () => { + jest.resetModules(); + configParser = await import('./index'); + defaultArgv = getArgv(); + defaultEnv = { RENOVATE_CONFIG_FILE: 'abc' }; + jest.mock('delay', () => Promise.resolve()); + }); + it('supports token in env', async () => { + const env: NodeJS.ProcessEnv = { ...defaultEnv, RENOVATE_TOKEN: 'abc' }; + const parsedConfig = await configParser.parseConfigs(env, defaultArgv); + expect(parsedConfig).toContainEntries([['token', 'abc']]); + }); + + it('supports token in CLI options', async () => { + defaultArgv = defaultArgv.concat([ + '--token=abc', + '--pr-footer=custom', + '--log-context=abc123', + ]); + const parsedConfig = await configParser.parseConfigs( + defaultEnv, + defaultArgv + ); + expect(parsedConfig).toContainEntries([ + ['token', 'abc'], + ['prFooter', 'custom'], + ['logContext', 'abc123'], + ]); + }); + + it('supports forceCli', async () => { + defaultArgv = defaultArgv.concat(['--force-cli=false']); + const env: NodeJS.ProcessEnv = { + ...defaultEnv, + RENOVATE_TOKEN: 'abc', + }; + const parsedConfig = await configParser.parseConfigs(env, defaultArgv); + expect(parsedConfig).toContainEntries([ + ['token', 'abc'], + ['force', null], + ]); + expect(parsedConfig).not.toContainKey('configFile'); + }); + it('supports config.force', async () => { + const configPath = upath.join(__dirname, '__fixtures__/with-force.js'); + const env: NodeJS.ProcessEnv = { + ...defaultEnv, + RENOVATE_CONFIG_FILE: configPath, + }; + const parsedConfig = await configParser.parseConfigs(env, defaultArgv); + expect(parsedConfig).toContainEntries([ + ['token', 'abcdefg'], + [ + 'force', + { + schedule: null, + }, + ], + ]); + }); + it('reads private key from file', async () => { + const privateKeyPath = upath.join(__dirname, '__fixtures__/private.pem'); + const env: NodeJS.ProcessEnv = { + ...defaultEnv, + RENOVATE_PRIVATE_KEY_PATH: privateKeyPath, + }; + const expected = await readFile(privateKeyPath); + const parsedConfig = await configParser.parseConfigs(env, defaultArgv); + + expect(parsedConfig).toContainEntries([['privateKey', expected]]); + }); + it('supports Bitbucket username/passwod', async () => { + defaultArgv = defaultArgv.concat([ + '--platform=bitbucket', + '--username=user', + '--password=pass', + ]); + const parsedConfig = await configParser.parseConfigs( + defaultEnv, + defaultArgv + ); + expect(parsedConfig).toContainEntries([ + ['platform', 'bitbucket'], + ['username', 'user'], + ['password', 'pass'], + ]); + }); + it('massages trailing slash into endpoint', async () => { + defaultArgv = defaultArgv.concat([ + '--endpoint=https://github.renovatebot.com/api/v3', + ]); + const parsed = await configParser.parseConfigs(defaultEnv, defaultArgv); + expect(parsed.endpoint).toEqual('https://github.renovatebot.com/api/v3/'); + }); + }); +}); diff --git a/lib/workers/global/config/parse/index.ts b/lib/workers/global/config/parse/index.ts new file mode 100644 index 00000000000000..ea32628c0e1161 --- /dev/null +++ b/lib/workers/global/config/parse/index.ts @@ -0,0 +1,88 @@ +import * as defaultsParser from '../../../../config/defaults'; +import { AllConfig } from '../../../../config/types'; +import { mergeChildConfig } from '../../../../config/utils'; +import { addStream, logger, setContext } from '../../../../logger'; +import { ensureDir, getSubDirectory, readFile } from '../../../../util/fs'; +import { ensureTrailingSlash } from '../../../../util/url'; +import * as cliParser from './cli'; +import * as envParser from './env'; +import * as fileParser from './file'; + +export async function parseConfigs( + env: NodeJS.ProcessEnv, + argv: string[] +): Promise { + logger.debug('Parsing configs'); + + // Get configs + const defaultConfig = defaultsParser.getConfig(); + const fileConfig = fileParser.getConfig(env); + const cliConfig = cliParser.getConfig(argv); + const envConfig = envParser.getConfig(env); + + let config: AllConfig = mergeChildConfig(fileConfig, envConfig); + config = mergeChildConfig(config, cliConfig); + + const combinedConfig = config; + + config = mergeChildConfig(defaultConfig, config); + + if (config.forceCli) { + const forcedCli = { ...cliConfig }; + delete forcedCli.token; + delete forcedCli.hostRules; + if (config.force) { + config.force = { ...config.force, ...forcedCli }; + } else { + config.force = forcedCli; + } + } + + if (!config.privateKey && config.privateKeyPath) { + config.privateKey = await readFile(config.privateKeyPath); + delete config.privateKeyPath; + } + + if (config.logContext) { + // This only has an effect if logContext was defined via file or CLI, otherwise it would already have been detected in env + setContext(config.logContext); + } + + // Add file logger + // istanbul ignore if + if (config.logFile) { + logger.debug( + `Enabling ${config.logFileLevel} logging to ${config.logFile}` + ); + await ensureDir(getSubDirectory(config.logFile)); + addStream({ + name: 'logfile', + path: config.logFile, + level: config.logFileLevel, + }); + } + + logger.trace({ config: defaultConfig }, 'Default config'); + logger.debug({ config: fileConfig }, 'File config'); + logger.debug({ config: cliConfig }, 'CLI config'); + logger.debug({ config: envConfig }, 'Env config'); + logger.debug({ config: combinedConfig }, 'Combined config'); + + // Get global config + logger.trace({ config }, 'Full config'); + + // Print config + logger.trace({ config }, 'Global config'); + + // Massage endpoint to have a trailing slash + if (config.endpoint) { + logger.debug('Adding trailing slash to endpoint'); + config.endpoint = ensureTrailingSlash(config.endpoint); + } + + // Remove log file entries + delete config.logFile; + delete config.logFileLevel; + + return config; +} diff --git a/lib/workers/global/index.spec.ts b/lib/workers/global/index.spec.ts index 2778446ca423a6..624ee5784f7cdf 100644 --- a/lib/workers/global/index.spec.ts +++ b/lib/workers/global/index.spec.ts @@ -1,6 +1,5 @@ import { ERROR, WARN } from 'bunyan'; import { getName, logger } from '../../../test/util'; -import * as _configParser from '../../config'; import { PLATFORM_TYPE_GITHUB, PLATFORM_TYPE_GITLAB, @@ -8,6 +7,7 @@ import { import * as datasourceDocker from '../../datasource/docker'; import * as _platform from '../../platform'; import * as _repositoryWorker from '../repository'; +import * as _configParser from './config/parse'; import * as _limits from './limits'; import * as globalWorker from '.'; diff --git a/lib/workers/global/index.ts b/lib/workers/global/index.ts index 6d096d4f8c4ce3..d683f318669cb7 100644 --- a/lib/workers/global/index.ts +++ b/lib/workers/global/index.ts @@ -8,16 +8,16 @@ import * as configParser from '../../config'; import { resolveConfigPresets } from '../../config/presets'; import { validateConfigSecrets } from '../../config/secrets'; import type { - GlobalConfig, + AllConfig, RenovateConfig, RenovateRepository, } from '../../config/types'; import { CONFIG_PRESETS_INVALID } from '../../constants/error-messages'; import { getProblems, logger, setMeta } from '../../logger'; -import { setExecConfig } from '../../util/exec'; import * as hostRules from '../../util/host-rules'; import * as repositoryWorker from '../repository'; import { autodiscoverRepositories } from './autodiscover'; +import { parseConfigs } from './config/parse'; import { globalFinalize, globalInitialize } from './initialize'; import { Limit, isLimitReached } from './limits'; @@ -39,7 +39,7 @@ export async function getRepositoryConfig( } function getGlobalConfig(): Promise { - return configParser.parseConfigs(process.env, process.argv); + return parseConfigs(process.env, process.argv); } function haveReachedLimits(): boolean { @@ -72,7 +72,7 @@ function checkEnv(): void { } } -export async function validatePresets(config: GlobalConfig): Promise { +export async function validatePresets(config: AllConfig): Promise { try { await resolveConfigPresets(config); } catch (err) /* istanbul ignore next */ { @@ -81,7 +81,7 @@ export async function validatePresets(config: GlobalConfig): Promise { } export async function start(): Promise { - let config: GlobalConfig; + let config: AllConfig; try { // read global config from file, env and cli args config = await getGlobalConfig(); @@ -103,7 +103,6 @@ export async function start(): Promise { break; } const repoConfig = await getRepositoryConfig(config, repository); - await setExecConfig(repoConfig); if (repoConfig.hostRules) { hostRules.clear(); repoConfig.hostRules.forEach((rule) => hostRules.add(rule)); diff --git a/lib/workers/global/initialize.ts b/lib/workers/global/initialize.ts index 4f1e3286cebdfd..c82fc237bbd24c 100644 --- a/lib/workers/global/initialize.ts +++ b/lib/workers/global/initialize.ts @@ -1,15 +1,15 @@ import os from 'os'; import fs from 'fs-extra'; import upath from 'upath'; -import type { GlobalConfig, RenovateConfig } from '../../config/types'; +import type { AllConfig, RenovateConfig } from '../../config/types'; import { logger } from '../../logger'; import { initPlatform } from '../../platform'; import * as packageCache from '../../util/cache/package'; import { setEmojiConfig } from '../../util/emoji'; import { Limit, setMaxLimit } from './limits'; -async function setDirectories(input: GlobalConfig): Promise { - const config: GlobalConfig = { ...input }; +async function setDirectories(input: AllConfig): Promise { + const config: AllConfig = { ...input }; process.env.TMPDIR = process.env.RENOVATE_TMPDIR || os.tmpdir(); if (config.baseDir) { logger.debug('Using configured baseDir: ' + config.baseDir); diff --git a/lib/workers/pr/__snapshots__/index.spec.ts.snap b/lib/workers/pr/__snapshots__/index.spec.ts.snap index f3ac046eb730fd..19997363f4e8a0 100644 --- a/lib/workers/pr/__snapshots__/index.spec.ts.snap +++ b/lib/workers/pr/__snapshots__/index.spec.ts.snap @@ -64,7 +64,7 @@ Array [ "bbUseDefaultReviewers": true, "gitLabAutomerge": false, }, - "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | pin | \`1.0.0\` -> \`1.1.0\` |\\n\\n📌 **Important**: Renovate will wait until you have merged this Pin PR before creating any *upgrade* PRs for the affected packages. Add the preset \`:preserveSemverRanges\` to your config if you instead don't wish to pin dependencies.\\n\\n---\\n\\n### Release Notes\\n\\n
\\nrenovateapp/dummy\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n
\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" in timezone some timezone.\\n\\nđŸšĻ **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\\n\\nâ™ģī¸ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | pin | \`1.0.0\` -> \`1.1.0\` |\\n\\n📌 **Important**: Renovate will wait until you have merged this Pin PR before creating any *upgrade* PRs for the affected packages. Add the preset \`:preserveSemverRanges\` to your config if you instead don't wish to pin dependencies.\\n\\n---\\n\\n### Release Notes\\n\\n
\\nrenovateapp/dummy\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n
\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" in timezone some timezone.\\n\\nđŸšĻ **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\\n\\nâ™ģ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "prTitle": "Update dependency dummy to v1.1.0", "sourceBranch": "renovate/dummy-1.x", "targetBranch": undefined, @@ -97,7 +97,7 @@ Array [ "bbUseDefaultReviewers": true, "gitLabAutomerge": false, }, - "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n
\\nrenovateapp/dummy\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n
\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\nđŸšĻ **Automerge**: Enabled.\\n\\nâ™ģī¸ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n
\\nrenovateapp/dummy\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n
\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\nđŸšĻ **Automerge**: Enabled.\\n\\nâ™ģ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "prTitle": "Update dependency dummy to v1.1.0", "sourceBranch": "renovate/dummy-1.x", "targetBranch": undefined, @@ -117,7 +117,7 @@ Array [ "bbUseDefaultReviewers": true, "gitLabAutomerge": false, }, - "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [gitlabdummy](https://dummy.com) ([source](https://gitlab.com/renovateapp/gitlabdummy), [changelog](https://gitlab.com/renovateapp/gitlabdummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\nđŸšĻ **Automerge**: Enabled.\\n\\nâ™ģī¸ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [gitlabdummy](https://dummy.com) ([source](https://gitlab.com/renovateapp/gitlabdummy), [changelog](https://gitlab.com/renovateapp/gitlabdummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\nđŸšĻ **Automerge**: Enabled.\\n\\nâ™ģ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "prTitle": "Update dependency dummy to v1.1.0", "sourceBranch": "renovate/gitlabdummy-1.x", "targetBranch": undefined, @@ -137,7 +137,7 @@ Array [ "bbUseDefaultReviewers": true, "gitLabAutomerge": false, }, - "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | lockFileMaintenance | \`1.0.0\` -> \`1.1.0\` |\\n| a | | | \`zzzzzz\` -> \`aaaaaaa\` |\\n| b | | pin | \`some_old_value\` -> \`some_new_value\` |\\n| c | | | \`\` -> \`\` |\\n| d | | lockFileMaintenance | \`\` -> \`\` |\\n\\nnote 1\\n\\nnote 2\\n\\n:warning: Release Notes retrieval for this PR were skipped because no github.com credentials were available.\\nIf you are using the hosted GitLab app, please follow [this guide](https://docs.renovatebot.com/install-gitlab-app/#configuring-a-token-for-githubcom-hosted-release-notes). If you are self-hosted, please see [this instruction](https://github.com/renovatebot/renovate/blob/master/docs/usage/self-hosting.md#githubcom-token-for-release-notes) instead.\\n\\n🔡 If you wish to disable git hash updates, add \`\\":disableDigestUpdates\\"\` to the extends array in your config.\\n\\n🔧 This Pull Request updates lock files to use the latest dependency versions.\\n\\n---\\n\\n### Release Notes\\n\\n
\\nrenovateapp/dummy\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n
\\n\\n
\\nrenovateapp/dummy\\n\\n
\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: At any time (no schedule defined).\\n\\nđŸšĻ **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\\n\\nâ™ģī¸ **Rebasing**: Never, or you tick the rebase/retry checkbox.\\n\\nđŸ‘ģ **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | lockFileMaintenance | \`1.0.0\` -> \`1.1.0\` |\\n| a | | | \`zzzzzz\` -> \`aaaaaaa\` |\\n| b | | pin | \`some_old_value\` -> \`some_new_value\` |\\n| c | | | \`\` -> \`\` |\\n| d | | lockFileMaintenance | \`\` -> \`\` |\\n\\nnote 1\\n\\nnote 2\\n\\n:warning: Release Notes retrieval for this PR were skipped because no github.com credentials were available.\\nIf you are using the hosted GitLab app, please follow [this guide](https://docs.renovatebot.com/install-gitlab-app/#configuring-a-token-for-githubcom-hosted-release-notes). If you are self-hosted, please see [this instruction](https://github.com/renovatebot/renovate/blob/master/docs/usage/self-hosting.md#githubcom-token-for-release-notes) instead.\\n\\n🔡 If you wish to disable git hash updates, add \`\\":disableDigestUpdates\\"\` to the extends array in your config.\\n\\n🔧 This Pull Request updates lock files to use the latest dependency versions.\\n\\n---\\n\\n### Release Notes\\n\\n
\\nrenovateapp/dummy\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n
\\n\\n
\\nrenovateapp/dummy\\n\\n
\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: At any time (no schedule defined).\\n\\nđŸšĻ **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\\n\\nâ™ģ **Rebasing**: Never, or you tick the rebase/retry checkbox.\\n\\nđŸ‘ģ **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "prTitle": "Update dependency dummy to v1.1.0", "sourceBranch": "renovate/dummy-1.x", "targetBranch": undefined, @@ -157,7 +157,7 @@ Array [ "bbUseDefaultReviewers": true, "gitLabAutomerge": false, }, - "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n
\\nsomeproject\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n
\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: At any time (no schedule defined).\\n\\nđŸšĻ **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\\n\\nâ™ģī¸ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "prBody": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n
\\nsomeproject\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n
\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: At any time (no schedule defined).\\n\\nđŸšĻ **Automerge**: Disabled by config. Please merge this manually once you are satisfied.\\n\\nâ™ģ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "prTitle": "Update dependency dummy to v1.1.0", "sourceBranch": "renovate/dummy-1.x", "targetBranch": undefined, @@ -231,7 +231,7 @@ Array [ exports[`workers/pr/index ensurePr should return modified existing PR 1`] = ` Object { - "body": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n
\\nrenovateapp/dummy\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n
\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\nđŸšĻ **Automerge**: Enabled.\\n\\nâ™ģī¸ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "body": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n
\\nrenovateapp/dummy\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n
\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\nđŸšĻ **Automerge**: Enabled.\\n\\nâ™ģ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "displayNumber": "Existing PR", "title": "Update dependency dummy to v1.1.0", } @@ -239,7 +239,7 @@ Object { exports[`workers/pr/index ensurePr should return modified existing PR title 1`] = ` Object { - "body": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n
\\nrenovateapp/dummy\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n
\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\nđŸšĻ **Automerge**: Enabled.\\n\\nâ™ģī¸ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", + "body": "This PR contains the following updates:\\n\\n| Package | Type | Update | Change |\\n|---|---|---|---|\\n| [dummy](https://dummy.com) ([source](https://github.com/renovateapp/dummy), [changelog](https://github.com/renovateapp/dummy/changelog.md)) | devDependencies | minor | \`1.0.0\` -> \`1.1.0\` |\\n\\n---\\n\\n### Release Notes\\n\\n
\\nrenovateapp/dummy\\n\\n### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)\\n\\n
\\n\\n---\\n\\n### Configuration\\n\\n📅 **Schedule**: \\"before 5am\\" (UTC).\\n\\nđŸšĻ **Automerge**: Enabled.\\n\\nâ™ģ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.\\n\\n🔕 **Ignore**: Close this PR and you won't be reminded about this update again.\\n\\n---\\n\\n - [ ] If you want to rebase/retry this PR, check this box.\\n\\n---\\n\\nThis PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).", "displayNumber": "Existing PR", "title": "wrong", } diff --git a/lib/workers/pr/body/__snapshots__/controls.spec.ts.snap b/lib/workers/pr/body/__snapshots__/controls.spec.ts.snap index 41e5203f65f86f..63dd7e7a0066ab 100644 --- a/lib/workers/pr/body/__snapshots__/controls.spec.ts.snap +++ b/lib/workers/pr/body/__snapshots__/controls.spec.ts.snap @@ -5,7 +5,7 @@ exports[`workers/pr/body/controls getControls when the branch is modified has t --- - - [ ] If you want to rebase/retry this PR, check this box. ⚠ī¸ **Warning**: custom changes will be lost. + - [ ] If you want to rebase/retry this PR, check this box. ⚠ **Warning**: custom changes will be lost. " `; diff --git a/lib/workers/pr/body/controls.ts b/lib/workers/pr/body/controls.ts index 5d7d5943f79270..2ee42f83560c96 100644 --- a/lib/workers/pr/body/controls.ts +++ b/lib/workers/pr/body/controls.ts @@ -1,4 +1,4 @@ -import { emojify } from 'node-emoji'; +import { emojify } from '../../../util/emoji'; import { isBranchModified } from '../../../util/git'; import { BranchConfig } from '../../types'; diff --git a/lib/workers/pr/changelog/gitlab.spec.ts b/lib/workers/pr/changelog/gitlab.spec.ts index b629a1f8c6a216..1cd9a111ec645c 100644 --- a/lib/workers/pr/changelog/gitlab.spec.ts +++ b/lib/workers/pr/changelog/gitlab.spec.ts @@ -34,7 +34,6 @@ const matchHost = 'https://gitlab.com/'; describe(getName(), () => { describe('getChangeLogJSON', () => { beforeEach(() => { - httpMock.setup(); hostRules.clear(); hostRules.add({ hostType: PLATFORM_TYPE_GITLAB, @@ -42,9 +41,7 @@ describe(getName(), () => { token: 'abc', }); }); - afterEach(() => { - httpMock.reset(); - }); + it('returns null if @types', async () => { httpMock.scope(matchHost); expect( diff --git a/lib/workers/pr/changelog/index.spec.ts b/lib/workers/pr/changelog/index.spec.ts index 62a2e9e2975ce2..5b41affe66adda 100644 --- a/lib/workers/pr/changelog/index.spec.ts +++ b/lib/workers/pr/changelog/index.spec.ts @@ -34,7 +34,6 @@ const upgrade: BranchConfig = partial({ describe(getName(), () => { describe('getChangeLogJSON', () => { beforeEach(() => { - httpMock.setup(); hostRules.clear(); hostRules.add({ hostType: PLATFORM_TYPE_GITHUB, @@ -43,10 +42,6 @@ describe(getName(), () => { }); }); - afterEach(() => { - httpMock.reset(); - }); - it('returns null if @types', async () => { httpMock.scope(githubApiHost); expect( diff --git a/lib/workers/pr/changelog/release-notes.spec.ts b/lib/workers/pr/changelog/release-notes.spec.ts index 7929098c49c986..15b4cbba12cf83 100644 --- a/lib/workers/pr/changelog/release-notes.spec.ts +++ b/lib/workers/pr/changelog/release-notes.spec.ts @@ -38,13 +38,11 @@ const gitlabTreeResponse = [ describe(getName(), () => { beforeEach(() => { - httpMock.setup(); hostRules.find.mockReturnValue({}); hostRules.hosts.mockReturnValue([]); }); afterEach(() => { - httpMock.reset(); jest.resetAllMocks(); }); diff --git a/lib/workers/pr/index.spec.ts b/lib/workers/pr/index.spec.ts index edd068b3dcf57e..4d0e5f62ea85a0 100644 --- a/lib/workers/pr/index.spec.ts +++ b/lib/workers/pr/index.spec.ts @@ -4,7 +4,7 @@ import { PLATFORM_TYPE_GITLAB } from '../../constants/platforms'; import { Pr, platform as _platform } from '../../platform'; import { BranchStatus } from '../../types'; import * as _limits from '../global/limits'; -import { BranchConfig, PrResult } from '../types'; +import type { BranchConfig } from '../types'; import * as prAutomerge from './automerge'; import * as _changelogHelper from './changelog'; import { getChangeLogJSON } from './changelog'; @@ -206,28 +206,27 @@ describe(getName(), () => { afterEach(() => { jest.clearAllMocks(); }); - it('should return null if check fails', async () => { + it('should return PR if update fails', async () => { platform.updatePr.mockImplementationOnce(() => { throw new Error('oops'); }); config.newValue = '1.2.0'; platform.getBranchPr.mockResolvedValueOnce(existingPr); - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Error); - expect(pr).toBeUndefined(); + const { pr } = await prWorker.ensurePr(config); + expect(pr).toBeDefined(); }); it('should return null if waiting for success', async () => { platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.red); config.prCreation = 'status-success'; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.AwaitingGreenBranch); + const { prBlockedBy, pr } = await prWorker.ensurePr(config); + expect(prBlockedBy).toEqual('AwaitingTests'); expect(pr).toBeUndefined(); }); it('should return needs-approval if prCreation set to approval', async () => { platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green); config.prCreation = 'approval'; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.AwaitingApproval); + const { prBlockedBy, pr } = await prWorker.ensurePr(config); + expect(prBlockedBy).toEqual('NeedsApproval'); expect(pr).toBeUndefined(); }); it('should create PR if success for gitlab deps', async () => { @@ -241,8 +240,7 @@ describe(getName(), () => { config.prCreation = 'status-success'; config.automerge = true; config.schedule = ['before 5am']; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.createPr.mock.calls[0]).toMatchSnapshot(); existingPr.body = platform.createPr.mock.calls[0][0].prBody; @@ -257,8 +255,7 @@ describe(getName(), () => { config.prCreation = 'status-success'; config.automerge = true; config.schedule = ['before 5am']; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.createPr.mock.calls[0]).toMatchSnapshot(); existingPr.body = platform.createPr.mock.calls[0][0].prBody; @@ -270,8 +267,8 @@ describe(getName(), () => { config.automerge = true; config.schedule = ['before 5am']; limits.isLimitReached.mockReturnValueOnce(true); - const { prResult } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.LimitReached); + const { prBlockedBy } = await prWorker.ensurePr(config); + expect(prBlockedBy).toEqual('RateLimited'); expect(platform.createPr.mock.calls).toBeEmpty(); }); it('should create PR if limit is reached but dashboard checked', async () => { @@ -281,11 +278,10 @@ describe(getName(), () => { config.automerge = true; config.schedule = ['before 5am']; limits.isLimitReached.mockReturnValueOnce(true); - const { prResult } = await prWorker.ensurePr({ + await prWorker.ensurePr({ ...config, dependencyDashboardChecks: { 'renovate/dummy-1.x': 'true' }, }); - expect(prResult).toEqual(PrResult.Created); expect(platform.createPr).toHaveBeenCalled(); }); it('should create group PR', async () => { @@ -319,8 +315,7 @@ describe(getName(), () => { for (const upgrade of config.upgrades) { upgrade.logJSON = await getChangeLogJSON(upgrade); } - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.createPr.mock.calls[0]).toMatchSnapshot(); }); @@ -333,8 +328,7 @@ describe(getName(), () => { config.timezone = 'some timezone'; config.rebaseWhen = 'behind-base-branch'; config.logJSON = await getChangeLogJSON(config); - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.createPr.mock.calls[0]).toMatchSnapshot(); expect(platform.createPr.mock.calls[0][0].prBody).toContain( @@ -348,8 +342,8 @@ describe(getName(), () => { throw new Error('Validation Failed (422)'); }); config.prCreation = 'status-success'; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Error); + const { prBlockedBy, pr } = await prWorker.ensurePr(config); + expect(prBlockedBy).toEqual('Error'); expect(pr).toBeUndefined(); }); it('should return null if waiting for not pending', async () => { @@ -358,8 +352,8 @@ describe(getName(), () => { Promise.resolve(new Date()) ); config.prCreation = 'not-pending'; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.AwaitingNotPending); + const { prBlockedBy, pr } = await prWorker.ensurePr(config); + expect(prBlockedBy).toEqual('AwaitingTests'); expect(pr).toBeUndefined(); }); it('should not create PR if waiting for not pending with stabilityStatus yellow', async () => { @@ -369,8 +363,8 @@ describe(getName(), () => { ); config.prCreation = 'not-pending'; config.stabilityStatus = BranchStatus.yellow; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.AwaitingNotPending); + const { prBlockedBy, pr } = await prWorker.ensurePr(config); + expect(prBlockedBy).toEqual('AwaitingTests'); expect(pr).toBeUndefined(); }); it('should create PR if pending timeout hit', async () => { @@ -380,27 +374,23 @@ describe(getName(), () => { ); config.prCreation = 'not-pending'; config.stabilityStatus = BranchStatus.yellow; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); }); it('should create PR if no longer pending', async () => { platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.red); config.prCreation = 'not-pending'; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); }); it('should create new branch if none exists', async () => { - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); }); it('should add assignees and reviewers to new PR', async () => { config.assignees = ['@foo', 'bar']; config.reviewers = ['baz', '@boo']; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.addAssignees).toHaveBeenCalledTimes(1); expect(platform.addAssignees.mock.calls).toMatchSnapshot(); @@ -446,8 +436,7 @@ describe(getName(), () => { }); config.assignees = ['@foo', 'bar']; config.reviewers = ['baz', '@boo']; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.addAssignees).toHaveBeenCalledTimes(1); expect(platform.addReviewers).toHaveBeenCalledTimes(1); @@ -458,8 +447,7 @@ describe(getName(), () => { }); config.assignees = ['@foo', 'bar']; config.reviewers = ['baz', '@boo']; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.addAssignees).toHaveBeenCalledTimes(1); expect(platform.addReviewers).toHaveBeenCalledTimes(1); @@ -468,8 +456,7 @@ describe(getName(), () => { config.assignees = ['bar']; config.reviewers = ['baz']; config.automerge = true; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.addAssignees).toHaveBeenCalledTimes(0); expect(platform.addReviewers).toHaveBeenCalledTimes(0); @@ -479,8 +466,7 @@ describe(getName(), () => { config.reviewers = ['baz']; config.automerge = true; config.assignAutomerge = true; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.addAssignees).toHaveBeenCalledTimes(1); expect(platform.addReviewers).toHaveBeenCalledTimes(1); @@ -490,8 +476,7 @@ describe(getName(), () => { config.assigneesSampleSize = 2; config.reviewers = ['baz', 'boo', 'bor']; config.reviewersSampleSize = 2; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.addAssignees).toHaveBeenCalledTimes(1); const assignees = platform.addAssignees.mock.calls[0][1]; @@ -508,8 +493,7 @@ describe(getName(), () => { config.assigneesSampleSize = 0; config.reviewers = ['baz', 'boo', 'bor']; config.reviewersSampleSize = 0; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.addAssignees).toHaveBeenCalledTimes(0); expect(platform.addReviewers).toHaveBeenCalledTimes(0); @@ -517,8 +501,7 @@ describe(getName(), () => { it('should add and deduplicate additionalReviewers on new PR', async () => { config.reviewers = ['@foo', 'bar']; config.additionalReviewers = ['bar', 'baz', '@boo']; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.addReviewers).toHaveBeenCalledTimes(1); expect(platform.addReviewers.mock.calls).toMatchSnapshot(); @@ -526,8 +509,7 @@ describe(getName(), () => { it('should add and deduplicate additionalReviewers to empty reviewers on new PR', async () => { config.reviewers = []; config.additionalReviewers = ['bar', 'baz', '@boo', '@foo', 'bar']; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.addReviewers).toHaveBeenCalledTimes(1); expect(platform.addReviewers.mock.calls).toMatchSnapshot(); @@ -538,8 +520,7 @@ describe(getName(), () => { config.automerge = true; config.schedule = ['before 5am']; config.logJSON = await getChangeLogJSON(config); - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.NotUpdated); + const { pr } = await prWorker.ensurePr(config); expect(platform.updatePr.mock.calls).toMatchSnapshot(); expect(platform.updatePr).toHaveBeenCalledTimes(0); expect(pr).toMatchObject(existingPr); @@ -553,8 +534,7 @@ describe(getName(), () => { config.automerge = true; config.schedule = ['before 5am']; config.logJSON = await getChangeLogJSON(config); - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.NotUpdated); + const { pr } = await prWorker.ensurePr(config); expect(platform.updatePr).toHaveBeenCalledTimes(0); expect(pr).toMatchObject(modifiedPr); }); @@ -564,8 +544,7 @@ describe(getName(), () => { config.schedule = ['before 5am']; config.logJSON = await getChangeLogJSON(config); platform.getBranchPr.mockResolvedValueOnce(existingPr); - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.NotUpdated); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchSnapshot(); }); it('should return modified existing PR title', async () => { @@ -574,8 +553,7 @@ describe(getName(), () => { ...existingPr, title: 'wrong', }); - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Updated); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchSnapshot(); }); it('should create PR if branch tests failed', async () => { @@ -583,8 +561,7 @@ describe(getName(), () => { config.automergeType = 'branch'; config.branchAutomergeFailureMessage = 'branch status error'; platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.red); - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); }); it('should create PR if branch automerging failed', async () => { @@ -592,8 +569,7 @@ describe(getName(), () => { config.automergeType = 'branch'; platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green); config.forcePr = true; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); }); it('should return no PR if branch automerging not failed', async () => { @@ -601,8 +577,8 @@ describe(getName(), () => { config.automergeType = 'branch'; platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.yellow); git.getBranchLastCommitTime.mockResolvedValueOnce(new Date()); - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.BlockedByBranchAutomerge); + const { prBlockedBy, pr } = await prWorker.ensurePr(config); + expect(prBlockedBy).toEqual('BranchAutomerge'); expect(pr).toBeUndefined(); }); it('should return PR if branch automerging taking too long', async () => { @@ -610,8 +586,7 @@ describe(getName(), () => { config.automergeType = 'branch'; platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.yellow); git.getBranchLastCommitTime.mockResolvedValueOnce(new Date('2018-01-01')); - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toBeDefined(); }); it('should return no PR if stabilityStatus yellow', async () => { @@ -620,14 +595,13 @@ describe(getName(), () => { config.stabilityStatus = BranchStatus.yellow; platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.yellow); git.getBranchLastCommitTime.mockResolvedValueOnce(new Date('2018-01-01')); - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.BlockedByBranchAutomerge); + const { prBlockedBy, pr } = await prWorker.ensurePr(config); + expect(prBlockedBy).toEqual('BranchAutomerge'); expect(pr).toBeUndefined(); }); it('handles duplicate upgrades', async () => { config.upgrades.push(config.upgrades[0]); - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); }); it('should create privateRepo PR if success', async () => { @@ -637,8 +611,7 @@ describe(getName(), () => { config.logJSON = await getChangeLogJSON(config); config.logJSON.project.gitlab = 'someproject'; delete config.logJSON.project.github; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); expect(platform.createPr.mock.calls[0]).toMatchSnapshot(); existingPr.body = platform.createPr.mock.calls[0][0].prBody; @@ -649,8 +622,7 @@ describe(getName(), () => { config.prCreation = 'not-pending'; config.artifactErrors = [{}]; config.platform = PLATFORM_TYPE_GITLAB; - const { prResult, pr } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); expect(pr).toMatchObject({ displayNumber: 'New Pull Request' }); }); @@ -667,8 +639,8 @@ describe(getName(), () => { it('should create a PR with set of labels and mergeable addLabels', async () => { config.labels = ['deps', 'renovate']; config.addLabels = ['deps', 'js']; - const { prResult } = await prWorker.ensurePr(config); - expect(prResult).toEqual(PrResult.Created); + const { pr } = await prWorker.ensurePr(config); + expect(pr).toBeDefined(); expect(platform.createPr.mock.calls[0][0]).toMatchObject({ labels: ['deps', 'renovate', 'js'], }); diff --git a/lib/workers/pr/index.ts b/lib/workers/pr/index.ts index 9d7cbbf3ce8873..b4bd2b75b9a236 100644 --- a/lib/workers/pr/index.ts +++ b/lib/workers/pr/index.ts @@ -10,10 +10,11 @@ import { PlatformPrOptions, Pr, platform } from '../../platform'; import { BranchStatus } from '../../types'; import { ExternalHostError } from '../../types/errors/external-host-error'; import { sampleSize } from '../../util'; +import { stripEmojis } from '../../util/emoji'; import { deleteBranch, getBranchLastCommitTime } from '../../util/git'; import * as template from '../../util/template'; import { Limit, incLimitedValue, isLimitReached } from '../global/limits'; -import { BranchConfig, PrResult } from '../types'; +import type { BranchConfig, PrBlockedBy } from '../types'; import { getPrBody } from './body'; import { ChangeLogError } from './changelog/types'; import { codeOwnersForPr } from './code-owners'; @@ -125,11 +126,19 @@ export function getPlatformPrOptions( config.gitLabAutomerge, }; } -export type EnsurePrResult = { - prResult: PrResult; - pr?: Pr; + +export type ResultWithPr = { + pr: Pr; + prBlockedBy?: never; +}; + +export type ResultWithoutPr = { + pr?: never; + prBlockedBy: PrBlockedBy; }; +export type EnsurePrResult = ResultWithPr | ResultWithoutPr; + // Ensures that PR exists with matching title/body export async function ensurePr( prConfig: BranchConfig @@ -195,7 +204,7 @@ export async function ensurePr( logger.debug(`Branch tests failed, so will create PR`); } else { // Branch should be automerged, so we don't want to create a PR - return { prResult: PrResult.BlockedByBranchAutomerge }; + return { prBlockedBy: 'BranchAutomerge' }; } } if (config.prCreation === 'status-success') { @@ -204,7 +213,7 @@ export async function ensurePr( logger.debug( `Branch status is "${await getBranchStatus()}" - not creating PR` ); - return { prResult: PrResult.AwaitingGreenBranch }; + return { prBlockedBy: 'AwaitingTests' }; } logger.debug('Branch status success'); } else if ( @@ -212,7 +221,7 @@ export async function ensurePr( !existingPr && dependencyDashboardCheck !== 'approvePr' ) { - return { prResult: PrResult.AwaitingApproval }; + return { prBlockedBy: 'NeedsApproval' }; } else if ( config.prCreation === 'not-pending' && !existingPr && @@ -238,7 +247,9 @@ export async function ensurePr( logger.debug( `Branch is ${elapsedHours} hours old - skipping PR creation` ); - return { prResult: PrResult.AwaitingNotPending }; + return { + prBlockedBy: 'AwaitingTests', + }; } const prNotPendingHours = String(config.prNotPendingHours); logger.debug( @@ -341,17 +352,19 @@ export async function ensurePr( logger.debug('Stripping Reviewable content'); existingPrBody = existingPrBody.slice(0, reviewableIndex); } + const existingPrTitle = stripEmojis(existingPr.title); + const newPrTitle = stripEmojis(prTitle); existingPrBody = existingPrBody.trim(); if ( - existingPr.title === prTitle && - noWhitespaceOrHeadings(existingPrBody) === - noWhitespaceOrHeadings(prBody) + existingPrTitle === newPrTitle && + noWhitespaceOrHeadings(stripEmojis(existingPrBody)) === + noWhitespaceOrHeadings(stripEmojis(prBody)) ) { logger.debug(`${existingPr.displayNumber} does not need updating`); - return { prResult: PrResult.NotUpdated, pr: existingPr }; + return { pr: existingPr }; } // PR must need updating - if (existingPr.title !== prTitle) { + if (existingPrTitle !== newPrTitle) { logger.debug( { branchName, @@ -380,7 +393,9 @@ export async function ensurePr( }); logger.info({ pr: existingPr.number, prTitle }, `PR updated`); } - return { prResult: PrResult.Updated, pr: existingPr }; + return { + pr: existingPr, + }; } logger.debug({ branch: branchName, prTitle }, `Creating PR`); // istanbul ignore if @@ -400,7 +415,7 @@ export async function ensurePr( !config.isVulnerabilityAlert ) { logger.debug('Skipping PR - limit reached'); - return { prResult: PrResult.LimitReached }; + return { prBlockedBy: 'RateLimited' }; } pr = await platform.createPr({ sourceBranch: branchName, @@ -426,7 +441,7 @@ export async function ensurePr( ) ) { logger.warn('A pull requests already exists'); - return { prResult: PrResult.ErrorAlreadyExists }; + return { prBlockedBy: 'Error' }; } if (err.statusCode === 502) { logger.warn( @@ -439,7 +454,7 @@ export async function ensurePr( await deleteBranch(branchName); } } - return { prResult: PrResult.Error }; + return { prBlockedBy: 'Error' }; } if ( config.branchAutomergeFailureMessage && @@ -477,7 +492,7 @@ export async function ensurePr( await addAssigneesReviewers(config, pr); } logger.debug(`Created ${pr.displayNumber}`); - return { prResult: PrResult.Created, pr }; + return { pr }; } catch (err) { // istanbul ignore if if ( @@ -491,5 +506,9 @@ export async function ensurePr( } logger.error({ err }, 'Failed to ensure PR: ' + prTitle); } - return { prResult: PrResult.Error }; + if (existingPr) { + return { pr: existingPr }; + } + // istanbul ignore next + return { prBlockedBy: 'Error' }; } diff --git a/lib/workers/repository/__fixtures__/master-issue_with_2_PR_edited.txt b/lib/workers/repository/__fixtures__/master-issue_with_2_PR_edited.txt index a3c51068a96acd..ba9398e96e0a7a 100644 --- a/lib/workers/repository/__fixtures__/master-issue_with_2_PR_edited.txt +++ b/lib/workers/repository/__fixtures__/master-issue_with_2_PR_edited.txt @@ -5,5 +5,5 @@ This issue contains a list of Renovate updates and their statuses. These updates have been manually edited so Renovate will no longer make changes. To discard all commits and start over, check the box below. - [ ] [pr1](../pull/1) - - [ ] pr2 (`dep2`, `dep3`) + - [ ] [pr2](../pull/2) (`dep2`, `dep3`) diff --git a/lib/workers/repository/__fixtures__/master-issue_with_3_PR_in_progress.txt b/lib/workers/repository/__fixtures__/master-issue_with_3_PR_in_progress.txt index 37d2fdc5b0d067..1f009661c7fe44 100644 --- a/lib/workers/repository/__fixtures__/master-issue_with_3_PR_in_progress.txt +++ b/lib/workers/repository/__fixtures__/master-issue_with_3_PR_in_progress.txt @@ -5,7 +5,7 @@ This issue contains a list of Renovate updates and their statuses. These updates have all been created already. Click a checkbox below to force a retry/rebase of any. - [ ] [pr1](../pull/1) - - [ ] pr2 (`dep2`, `dep3`) + - [ ] [pr2](../pull/2) (`dep2`, `dep3`) - [ ] [pr3](../pull/3) - [ ] **Check this option to rebase all the above open PRs at once** diff --git a/lib/workers/repository/__fixtures__/master-issue_with_8_PR.txt b/lib/workers/repository/__fixtures__/master-issue_with_8_PR.txt index 5bcb226c2e0c91..f9c831dee89680 100644 --- a/lib/workers/repository/__fixtures__/master-issue_with_8_PR.txt +++ b/lib/workers/repository/__fixtures__/master-issue_with_8_PR.txt @@ -27,3 +27,9 @@ These updates encountered an error and will be retried. Click a checkbox below t - [ ] pr7 - [ ] pr8 +## Pending Branch Automerge + +These updates await pending status checks before automerging. + + - [ ] pr9 + diff --git a/lib/workers/repository/__snapshots__/dependency-dashboard.spec.ts.snap b/lib/workers/repository/__snapshots__/dependency-dashboard.spec.ts.snap index 03aae83ed10094..fd57ff828b0c42 100644 --- a/lib/workers/repository/__snapshots__/dependency-dashboard.spec.ts.snap +++ b/lib/workers/repository/__snapshots__/dependency-dashboard.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`workers/repository/dependency-dashboard ensureMasterIssue() contains logged problems 1`] = ` +exports[`workers/repository/dependency-dashboard ensureDependencyDashboard() contains logged problems 1`] = ` "This issue contains a list of Renovate updates and their statuses. ## Repository problems @@ -22,7 +22,7 @@ These updates await pending status checks. To force their creation now, check th " `; -exports[`workers/repository/dependency-dashboard ensureMasterIssue() open or update Dependency Dashboard when all branches are closed and dependencyDashboardAutoclose is false 1`] = ` +exports[`workers/repository/dependency-dashboard ensureDependencyDashboard() open or update Dependency Dashboard when all branches are closed and dependencyDashboardAutoclose is false 1`] = ` "This issue contains a list of Renovate updates and their statuses. This repository currently has no open or pending branches. @@ -32,7 +32,7 @@ And this is a footer " `; -exports[`workers/repository/dependency-dashboard ensureMasterIssue() open or update Dependency Dashboard when rules contain approvals 1`] = ` +exports[`workers/repository/dependency-dashboard ensureDependencyDashboard() open or update Dependency Dashboard when rules contain approvals 1`] = ` "This issue contains a list of Renovate updates and their statuses. This repository currently has no open or pending branches. @@ -41,3 +41,15 @@ This repository currently has no open or pending branches. And this is a footer " `; + +exports[`workers/repository/dependency-dashboard readDashboardBody() reads dashboard body 1`] = ` +Object { + "dependencyDashboardChecks": Object { + "branchName1": "approve", + }, + "dependencyDashboardIssue": 1, + "dependencyDashboardRebaseAllOpen": true, + "dependencyDashboardTitle": "Dependency Dashboard", + "prCreation": "approval", +} +`; diff --git a/lib/workers/repository/dependency-dashboard.spec.ts b/lib/workers/repository/dependency-dashboard.spec.ts index c1c5e974621d66..f3e2fb2512387b 100644 --- a/lib/workers/repository/dependency-dashboard.spec.ts +++ b/lib/workers/repository/dependency-dashboard.spec.ts @@ -10,8 +10,7 @@ import { } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import { PLATFORM_TYPE_GITHUB } from '../../constants/platforms'; -import { Platform, Pr } from '../../platform'; -import { PrState } from '../../types'; +import type { Platform } from '../../platform'; import { BranchConfig, BranchResult, BranchUpgradeConfig } from '../types'; import * as dependencyDashboard from './dependency-dashboard'; @@ -31,39 +30,49 @@ async function dryRun( // eslint-disable-next-line @typescript-eslint/no-shadow platform: jest.Mocked, ensureIssueClosingCalls = 0, - ensureIssueCalls = 0, - getBranchPrCalls = 0, - findPrCalls = 0 + ensureIssueCalls = 0 ) { jest.clearAllMocks(); setAdminConfig({ dryRun: true }); - await dependencyDashboard.ensureMasterIssue(config, branches); + await dependencyDashboard.ensureDependencyDashboard(config, branches); expect(platform.ensureIssueClosing).toHaveBeenCalledTimes( ensureIssueClosingCalls ); expect(platform.ensureIssue).toHaveBeenCalledTimes(ensureIssueCalls); - expect(platform.getBranchPr).toHaveBeenCalledTimes(getBranchPrCalls); - expect(platform.findPr).toHaveBeenCalledTimes(findPrCalls); } describe(getName(), () => { - describe('ensureMasterIssue()', () => { + describe('readDashboardBody()', () => { + it('reads dashboard body', async () => { + const conf: RenovateConfig = {}; + conf.prCreation = 'approval'; + platform.findIssue.mockResolvedValueOnce({ + title: '', + number: 1, + body: + loadFixture('master-issue_with_8_PR.txt').replace('- [ ]', '- [x]') + + '\n\n - [x] ', + }); + await dependencyDashboard.readDashboardBody(conf); + expect(conf).toMatchSnapshot(); + }); + }); + + describe('ensureDependencyDashboard()', () => { beforeEach(() => { setAdminConfig(); }); - it('do nothing if masterissue is disable', async () => { + it('do nothing if dependencyDashboard is disabled', async () => { const branches: BranchConfig[] = []; - await dependencyDashboard.ensureMasterIssue(config, branches); + await dependencyDashboard.ensureDependencyDashboard(config, branches); expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0); expect(platform.ensureIssue).toHaveBeenCalledTimes(0); - expect(platform.getBranchPr).toHaveBeenCalledTimes(0); - expect(platform.findPr).toHaveBeenCalledTimes(0); // same with dry run await dryRun(branches, platform); }); - it('do nothing if it has no masterissueapproval branches', async () => { + it('do nothing if it has no dependencyDashboardApproval branches', async () => { const branches = [ { ...mock(), @@ -75,11 +84,9 @@ describe(getName(), () => { dependencyDashboardApproval: false, }, ]; - await dependencyDashboard.ensureMasterIssue(config, branches); + await dependencyDashboard.ensureDependencyDashboard(config, branches); expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0); expect(platform.ensureIssue).toHaveBeenCalledTimes(0); - expect(platform.getBranchPr).toHaveBeenCalledTimes(0); - expect(platform.findPr).toHaveBeenCalledTimes(0); // same with dry run await dryRun(branches, platform); @@ -89,14 +96,12 @@ describe(getName(), () => { const branches: BranchConfig[] = []; config.dependencyDashboard = true; config.dependencyDashboardAutoclose = true; - await dependencyDashboard.ensureMasterIssue(config, branches); + await dependencyDashboard.ensureDependencyDashboard(config, branches); expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(1); expect(platform.ensureIssueClosing.mock.calls[0][0]).toBe( config.dependencyDashboardTitle ); expect(platform.ensureIssue).toHaveBeenCalledTimes(0); - expect(platform.getBranchPr).toHaveBeenCalledTimes(0); - expect(platform.findPr).toHaveBeenCalledTimes(0); // same with dry run await dryRun(branches, platform); @@ -118,14 +123,12 @@ describe(getName(), () => { ]; config.dependencyDashboard = true; config.dependencyDashboardAutoclose = true; - await dependencyDashboard.ensureMasterIssue(config, branches); + await dependencyDashboard.ensureDependencyDashboard(config, branches); expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(1); expect(platform.ensureIssueClosing.mock.calls[0][0]).toBe( config.dependencyDashboardTitle ); expect(platform.ensureIssue).toHaveBeenCalledTimes(0); - expect(platform.getBranchPr).toHaveBeenCalledTimes(0); - expect(platform.findPr).toHaveBeenCalledTimes(0); // same with dry run await dryRun(branches, platform); @@ -135,15 +138,13 @@ describe(getName(), () => { const branches: BranchConfig[] = []; config.dependencyDashboard = true; config.dependencyDashboardFooter = 'And this is a footer'; - await dependencyDashboard.ensureMasterIssue(config, branches); + await dependencyDashboard.ensureDependencyDashboard(config, branches); expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0); expect(platform.ensureIssue).toHaveBeenCalledTimes(1); expect(platform.ensureIssue.mock.calls[0][0].title).toBe( config.dependencyDashboardTitle ); expect(platform.ensureIssue.mock.calls[0][0].body).toMatchSnapshot(); - expect(platform.getBranchPr).toHaveBeenCalledTimes(0); - expect(platform.findPr).toHaveBeenCalledTimes(0); // same with dry run await dryRun(branches, platform); @@ -158,15 +159,13 @@ describe(getName(), () => { {}, ]; config.dependencyDashboardFooter = 'And this is a footer'; - await dependencyDashboard.ensureMasterIssue(config, branches); + await dependencyDashboard.ensureDependencyDashboard(config, branches); expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0); expect(platform.ensureIssue).toHaveBeenCalledTimes(1); expect(platform.ensureIssue.mock.calls[0][0].title).toBe( config.dependencyDashboardTitle ); expect(platform.ensureIssue.mock.calls[0][0].body).toMatchSnapshot(); - expect(platform.getBranchPr).toHaveBeenCalledTimes(0); - expect(platform.findPr).toHaveBeenCalledTimes(0); // same with dry run await dryRun(branches, platform); @@ -230,9 +229,17 @@ describe(getName(), () => { result: BranchResult.Error, branchName: 'branchName8', }, + { + ...mock(), + prTitle: 'pr9', + upgrades: [{ ...mock(), depName: 'dep9' }], + result: BranchResult.Done, + prBlockedBy: 'BranchAutomerge', + branchName: 'branchName9', + }, ]; config.dependencyDashboard = true; - await dependencyDashboard.ensureMasterIssue(config, branches); + await dependencyDashboard.ensureDependencyDashboard(config, branches); expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0); expect(platform.ensureIssue).toHaveBeenCalledTimes(1); expect(platform.ensureIssue.mock.calls[0][0].title).toBe( @@ -241,8 +248,6 @@ describe(getName(), () => { expect(platform.ensureIssue.mock.calls[0][0].body).toBe( loadFixture('master-issue_with_8_PR.txt') ); - expect(platform.getBranchPr).toHaveBeenCalledTimes(0); - expect(platform.findPr).toHaveBeenCalledTimes(0); // same with dry run await dryRun(branches, platform); @@ -252,6 +257,7 @@ describe(getName(), () => { const branches: BranchConfig[] = [ { ...mock(), + prNo: 1, prTitle: 'pr1', upgrades: [{ ...mock(), depName: 'dep1' }], result: BranchResult.PrEdited, @@ -259,6 +265,7 @@ describe(getName(), () => { }, { ...mock(), + prNo: 2, prTitle: 'pr2', upgrades: [ { ...mock(), depName: 'dep2' }, @@ -269,10 +276,7 @@ describe(getName(), () => { }, ]; config.dependencyDashboard = true; - platform.getBranchPr - .mockResolvedValueOnce({ ...mock(), number: 1 }) - .mockResolvedValueOnce(undefined); - await dependencyDashboard.ensureMasterIssue(config, branches); + await dependencyDashboard.ensureDependencyDashboard(config, branches); expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0); expect(platform.ensureIssue).toHaveBeenCalledTimes(1); expect(platform.ensureIssue.mock.calls[0][0].title).toBe( @@ -281,13 +285,9 @@ describe(getName(), () => { expect(platform.ensureIssue.mock.calls[0][0].body).toBe( loadFixture('master-issue_with_2_PR_edited.txt') ); - expect(platform.getBranchPr).toHaveBeenCalledTimes(2); - expect(platform.getBranchPr.mock.calls[0][0]).toBe('branchName1'); - expect(platform.getBranchPr.mock.calls[1][0]).toBe('branchName2'); - expect(platform.findPr).toHaveBeenCalledTimes(0); // same with dry run - await dryRun(branches, platform, 0, 0, 2, 0); + await dryRun(branches, platform, 0, 0); }); it('checks an issue with 3 PR in progress and rebase all option', async () => { @@ -297,11 +297,13 @@ describe(getName(), () => { prTitle: 'pr1', upgrades: [{ ...mock(), depName: 'dep1' }], result: BranchResult.Rebase, + prNo: 1, branchName: 'branchName1', }, { ...mock(), prTitle: 'pr2', + prNo: 2, upgrades: [ { ...mock(), depName: 'dep2' }, { ...mock(), depName: 'dep3' }, @@ -312,17 +314,14 @@ describe(getName(), () => { { ...mock(), prTitle: 'pr3', + prNo: 3, upgrades: [{ ...mock(), depName: 'dep3' }], result: BranchResult.Rebase, branchName: 'branchName3', }, ]; config.dependencyDashboard = true; - platform.getBranchPr - .mockResolvedValueOnce({ ...mock(), number: 1 }) - .mockResolvedValueOnce(undefined) - .mockResolvedValueOnce({ ...mock(), number: 3 }); - await dependencyDashboard.ensureMasterIssue(config, branches); + await dependencyDashboard.ensureDependencyDashboard(config, branches); expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0); expect(platform.ensureIssue).toHaveBeenCalledTimes(1); expect(platform.ensureIssue.mock.calls[0][0].title).toBe( @@ -331,14 +330,9 @@ describe(getName(), () => { expect(platform.ensureIssue.mock.calls[0][0].body).toBe( loadFixture('master-issue_with_3_PR_in_progress.txt') ); - expect(platform.getBranchPr).toHaveBeenCalledTimes(3); - expect(platform.getBranchPr.mock.calls[0][0]).toBe('branchName1'); - expect(platform.getBranchPr.mock.calls[1][0]).toBe('branchName2'); - expect(platform.getBranchPr.mock.calls[2][0]).toBe('branchName3'); - expect(platform.findPr).toHaveBeenCalledTimes(0); // same with dry run - await dryRun(branches, platform, 0, 0, 3, 0); + await dryRun(branches, platform, 0, 0); }); it('checks an issue with 2 PR closed / ignored', async () => { @@ -362,11 +356,7 @@ describe(getName(), () => { }, ]; config.dependencyDashboard = true; - platform.getBranchPr - .mockResolvedValueOnce({ ...mock(), number: 1 }) - .mockResolvedValueOnce(undefined) - .mockResolvedValueOnce({ ...mock(), number: 3 }); - await dependencyDashboard.ensureMasterIssue(config, branches); + await dependencyDashboard.ensureDependencyDashboard(config, branches); expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0); expect(platform.ensureIssue).toHaveBeenCalledTimes(1); expect(platform.ensureIssue.mock.calls[0][0].title).toBe( @@ -375,17 +365,9 @@ describe(getName(), () => { expect(platform.ensureIssue.mock.calls[0][0].body).toBe( loadFixture('master-issue_with_2_PR_closed_ignored.txt') ); - expect(platform.getBranchPr).toHaveBeenCalledTimes(0); - expect(platform.findPr).toHaveBeenCalledTimes(2); - expect(platform.findPr.mock.calls[0][0].branchName).toBe('branchName1'); - expect(platform.findPr.mock.calls[0][0].prTitle).toBe('pr1'); - expect(platform.findPr.mock.calls[0][0].state).toBe(PrState.NotOpen); - expect(platform.findPr.mock.calls[1][0].branchName).toBe('branchName2'); - expect(platform.findPr.mock.calls[1][0].prTitle).toBe('pr2'); - expect(platform.findPr.mock.calls[1][0].state).toBe(PrState.NotOpen); // same with dry run - await dryRun(branches, platform, 0, 0, 0, 2); + await dryRun(branches, platform, 0, 0); }); it('checks an issue with 3 PR in approval', async () => { @@ -424,7 +406,7 @@ describe(getName(), () => { ]; config.dependencyDashboard = true; config.dependencyDashboardPrApproval = true; - await dependencyDashboard.ensureMasterIssue(config, branches); + await dependencyDashboard.ensureDependencyDashboard(config, branches); expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0); expect(platform.ensureIssue).toHaveBeenCalledTimes(1); expect(platform.ensureIssue.mock.calls[0][0].title).toBe( @@ -433,7 +415,6 @@ describe(getName(), () => { expect(platform.ensureIssue.mock.calls[0][0].body).toBe( loadFixture('master-issue_with_3_PR_in_approval.txt') ); - expect(platform.findPr).toHaveBeenCalledTimes(0); // same with dry run await dryRun(branches, platform); @@ -483,7 +464,7 @@ describe(getName(), () => { }, ]); config.dependencyDashboard = true; - await dependencyDashboard.ensureMasterIssue(config, branches); + await dependencyDashboard.ensureDependencyDashboard(config, branches); expect(platform.ensureIssue).toHaveBeenCalledTimes(1); expect(platform.ensureIssue.mock.calls[0][0].body).toMatchSnapshot(); }); diff --git a/lib/workers/repository/dependency-dashboard.ts b/lib/workers/repository/dependency-dashboard.ts index 8e75e00599a228..46e527b883ecd2 100644 --- a/lib/workers/repository/dependency-dashboard.ts +++ b/lib/workers/repository/dependency-dashboard.ts @@ -3,15 +3,61 @@ import { nameFromLevel } from 'bunyan'; import { getAdminConfig } from '../../config/admin'; import type { RenovateConfig } from '../../config/types'; import { getProblems, logger } from '../../logger'; -import { Pr, platform } from '../../platform'; -import { PrState } from '../../types'; +import { platform } from '../../platform'; import { BranchConfig, BranchResult } from '../types'; -function getListItem(branch: BranchConfig, type: string, pr?: Pr): string { +interface DependencyDashboard { + dependencyDashboardChecks: Record; + dependencyDashboardRebaseAllOpen: boolean; +} + +function parseDashboardIssue(issueBody: string): DependencyDashboard { + const checkMatch = ' - \\[x\\] '; + const checked = issueBody.match(new RegExp(checkMatch, 'g')); + const dependencyDashboardChecks: Record = {}; + if (checked?.length) { + const re = new RegExp(checkMatch); + checked.forEach((check) => { + const [, type, branchName] = re.exec(check); + dependencyDashboardChecks[branchName] = type; + }); + } + const checkedRebaseAll = issueBody.includes( + ' - [x] ' + ); + let dependencyDashboardRebaseAllOpen = false; + if (checkedRebaseAll) { + dependencyDashboardRebaseAllOpen = true; + /* eslint-enable no-param-reassign */ + } + return { dependencyDashboardChecks, dependencyDashboardRebaseAllOpen }; +} + +export async function readDashboardBody(config: RenovateConfig): Promise { + /* eslint-disable no-param-reassign */ + config.dependencyDashboardChecks = {}; + const stringifiedConfig = JSON.stringify(config); + if ( + config.dependencyDashboard || + stringifiedConfig.includes('"dependencyDashboardApproval":true') || + stringifiedConfig.includes('"prCreation":"approval"') + ) { + config.dependencyDashboardTitle = + config.dependencyDashboardTitle || `Dependency Dashboard`; + const issue = await platform.findIssue(config.dependencyDashboardTitle); + if (issue) { + config.dependencyDashboardIssue = issue.number; + Object.assign(config, parseDashboardIssue(issue.body)); + } + } + /* eslint-enable no-param-reassign */ +} + +function getListItem(branch: BranchConfig, type: string): string { let item = ' - [ ] '; item += ``; - if (pr) { - item += `[${branch.prTitle}](../pull/${pr.number})`; + if (branch.prNo) { + item += `[${branch.prTitle}](../pull/${branch.prNo})`; } else { item += branch.prTitle; } @@ -49,7 +95,7 @@ function appendRepoProblems(config: RenovateConfig, issueBody: string): string { return newIssueBody; } -export async function ensureMasterIssue( +export async function ensureDependencyDashboard( config: RenovateConfig, branches: BranchConfig[] ): Promise { @@ -166,8 +212,7 @@ export async function ensureMasterIssue( issueBody += '## Edited/Blocked\n\n'; issueBody += `These updates have been manually edited so Renovate will no longer make changes. To discard all commits and start over, check the box below.\n\n`; for (const branch of prEdited) { - const pr = await platform.getBranchPr(branch.branchName); - issueBody += getListItem(branch, 'rebase', pr); + issueBody += getListItem(branch, 'rebase'); } issueBody += '\n'; } @@ -182,6 +227,17 @@ export async function ensureMasterIssue( } issueBody += '\n'; } + const prPendingBranchAutomerge = branches.filter( + (branch) => branch.prBlockedBy === 'BranchAutomerge' + ); + if (prPendingBranchAutomerge.length) { + issueBody += '## Pending Branch Automerge\n\n'; + issueBody += `These updates await pending status checks before automerging.\n\n`; + for (const branch of prPendingBranchAutomerge) { + issueBody += getListItem(branch, 'approvePr'); + } + issueBody += '\n'; + } const otherRes = [ BranchResult.Pending, BranchResult.NeedsApproval, @@ -195,16 +251,40 @@ export async function ensureMasterIssue( BranchResult.Automerged, BranchResult.PrEdited, ]; - const inProgress = branches.filter( - (branch) => !otherRes.includes(branch.result) + let inProgress = branches.filter( + (branch) => + !otherRes.includes(branch.result) && + branch.prBlockedBy !== 'BranchAutomerge' + ); + const otherBranches = inProgress.filter( + (branch) => branch.prBlockedBy || !branch.prNo + ); + // istanbul ignore if + if (otherBranches.length) { + issueBody += '## Other Branches\n\n'; + issueBody += `These updates are pending. To force PRs open, check the box below.\n\n`; + for (const branch of otherBranches) { + logger.info( + { + prBlockedBy: branch.prBlockedBy, + prNo: branch.prNo, + result: branch.result, + }, + 'Blocked PR' + ); + issueBody += getListItem(branch, 'other'); + } + issueBody += '\n'; + } + inProgress = inProgress.filter( + (branch) => branch.prNo && !branch.prBlockedBy ); if (inProgress.length) { issueBody += '## Open\n\n'; issueBody += 'These updates have all been created already. Click a checkbox below to force a retry/rebase of any.\n\n'; for (const branch of inProgress) { - const pr = await platform.getBranchPr(branch.branchName); - issueBody += getListItem(branch, 'rebase', pr); + issueBody += getListItem(branch, 'rebase'); } if (inProgress.length > 2) { issueBody += ' - [ ] '; @@ -223,12 +303,7 @@ export async function ensureMasterIssue( issueBody += 'These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.\n\n'; for (const branch of alreadyExisted) { - const pr = await platform.findPr({ - branchName: branch.branchName, - prTitle: branch.prTitle, - state: PrState.NotOpen, - }); - issueBody += getListItem(branch, 'recreate', pr); + issueBody += getListItem(branch, 'recreate'); } issueBody += '\n'; } diff --git a/lib/workers/repository/index.ts b/lib/workers/repository/index.ts index ecce455412db6a..222e262be97c04 100644 --- a/lib/workers/repository/index.ts +++ b/lib/workers/repository/index.ts @@ -2,11 +2,12 @@ import fs from 'fs-extra'; import { getAdminConfig, setAdminConfig } from '../../config/admin'; import type { RenovateConfig } from '../../config/types'; import { logger, setMeta } from '../../logger'; +import { removeDanglingContainers } from '../../util/exec/docker'; import { deleteLocalFile, privateCacheDir } from '../../util/fs'; import * as queue from '../../util/http/queue'; import { addSplit, getSplits, splitInit } from '../../util/split'; import { setBranchCache } from './cache'; -import { ensureMasterIssue } from './dependency-dashboard'; +import { ensureDependencyDashboard } from './dependency-dashboard'; import handleError from './error'; import { finaliseRepo } from './finalise'; import { initRepo } from './init'; @@ -25,10 +26,12 @@ try { // istanbul ignore next export async function renovateRepository( - repoConfig: RenovateConfig + repoConfig: RenovateConfig, + canRetry = true ): Promise { splitInit(); let config = setAdminConfig(repoConfig); + await removeDanglingContainers(); setMeta({ repository: config.repository }); logger.info({ renovateVersion }, 'Repository started'); logger.trace({ config }); @@ -45,10 +48,18 @@ export async function renovateRepository( ); await ensureOnboardingPr(config, packageFiles, branches); const res = await updateRepo(config, branches); + setMeta({ repository: config.repository }); addSplit('update'); await setBranchCache(branches); - if (res !== 'automerged') { - await ensureMasterIssue(config, branches); + if (res === 'automerged') { + if (canRetry) { + logger.info('Renovating repository again after automerge result'); + const recursiveRes = await renovateRepository(repoConfig, false); + return recursiveRes; + } + logger.debug(`Automerged but already retried once`); + } else { + await ensureDependencyDashboard(config, branches); } await finaliseRepo(config, branchList); repoResult = processResult(config, res); diff --git a/lib/workers/repository/init/__snapshots__/merge.spec.ts.snap b/lib/workers/repository/init/__snapshots__/merge.spec.ts.snap index 8fe8176366dde6..2271e01caa04df 100644 --- a/lib/workers/repository/init/__snapshots__/merge.spec.ts.snap +++ b/lib/workers/repository/init/__snapshots__/merge.spec.ts.snap @@ -1,19 +1,76 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`workers/repository/init/merge detectRepoFileConfig() finds .github/renovate.json 1`] = `Promise {}`; +exports[`workers/repository/init/merge detectRepoFileConfig() finds .github/renovate.json 1`] = ` +Object { + "configFileName": ".github/renovate.json", + "configFileParsed": Object {}, +} +`; -exports[`workers/repository/init/merge detectRepoFileConfig() finds .gitlab/renovate.json 1`] = `Promise {}`; +exports[`workers/repository/init/merge detectRepoFileConfig() finds .gitlab/renovate.json 1`] = ` +Object { + "configFileName": ".gitlab/renovate.json", + "configFileParsed": Object {}, +} +`; -exports[`workers/repository/init/merge detectRepoFileConfig() finds .renovaterc.json 1`] = `Promise {}`; +exports[`workers/repository/init/merge detectRepoFileConfig() finds .renovaterc.json 1`] = ` +Object { + "configFileName": ".renovaterc.json", + "configFileParsed": Object {}, +} +`; -exports[`workers/repository/init/merge detectRepoFileConfig() finds and parse renovate.json5 1`] = `Promise {}`; +exports[`workers/repository/init/merge detectRepoFileConfig() finds .renovaterc.json 2`] = ` +Object { + "configFileName": ".renovaterc.json", + "configFileParsed": "{\\"something\\":\\"new\\"}", +} +`; -exports[`workers/repository/init/merge detectRepoFileConfig() returns config if not found 1`] = `Promise {}`; +exports[`workers/repository/init/merge detectRepoFileConfig() finds and parse renovate.json5 1`] = ` +Object { + "configFileName": "renovate.json5", + "configFileParsed": Object {}, +} +`; -exports[`workers/repository/init/merge detectRepoFileConfig() returns error if cannot parse 1`] = `Promise {}`; +exports[`workers/repository/init/merge detectRepoFileConfig() returns config if not found 1`] = `Object {}`; -exports[`workers/repository/init/merge detectRepoFileConfig() throws error if duplicate keys 1`] = `Promise {}`; +exports[`workers/repository/init/merge detectRepoFileConfig() returns error if cannot parse 1`] = ` +Object { + "configFileName": "renovate.json", + "configFileParseError": Object { + "validationError": "Invalid JSON (parsing failed)", + "validationMessage": "Syntax error near cannot par", + }, +} +`; -exports[`workers/repository/init/merge detectRepoFileConfig() uses package.json config if found 1`] = `Promise {}`; +exports[`workers/repository/init/merge detectRepoFileConfig() throws error if duplicate keys 1`] = ` +Object { + "configFileName": ".renovaterc", + "configFileParseError": Object { + "validationError": "Duplicate keys in JSON", + "validationMessage": "\\"Syntax error: duplicated keys \\\\\\"enabled\\\\\\" near \\\\\\": false }\\"", + }, +} +`; + +exports[`workers/repository/init/merge detectRepoFileConfig() uses package.json config if found 1`] = ` +Object { + "configFileName": "package.json", + "configFileParsed": Object { + "prHourlyLimit": 10, + }, +} +`; + +exports[`workers/repository/init/merge detectRepoFileConfig() uses package.json config if found 2`] = ` +Object { + "configFileName": "package.json", + "configFileParsed": undefined, +} +`; exports[`workers/repository/init/merge mergeRenovateConfig() throws error if misconfigured 1`] = `[Error: config-validation]`; diff --git a/lib/workers/repository/init/merge.spec.ts b/lib/workers/repository/init/merge.spec.ts index c55b93d36dc783..cf508943c1f8a4 100644 --- a/lib/workers/repository/init/merge.spec.ts +++ b/lib/workers/repository/init/merge.spec.ts @@ -5,9 +5,11 @@ import { getName, git, mocked, + platform, } from '../../../../test/util'; import * as _migrateAndValidate from '../../../config/migrate-validate'; import * as _migrate from '../../../config/migration'; +import { initialize } from '../../../util/cache/repository'; import { checkForRepoConfigError, detectRepoFileConfig, @@ -33,12 +35,16 @@ jest.mock('../../../config/migrate-validate'); describe(getName(), () => { describe('detectRepoFileConfig()', () => { - it('returns config if not found', () => { + beforeEach(async () => { + await initialize({}); + }); + + it('returns config if not found', async () => { git.getFileList.mockResolvedValue(['package.json']); fs.readLocalFile.mockResolvedValue('{}'); - expect(detectRepoFileConfig()).toMatchSnapshot(); + expect(await detectRepoFileConfig()).toMatchSnapshot(); }); - it('uses package.json config if found', () => { + it('uses package.json config if found', async () => { git.getFileList.mockResolvedValue(['package.json']); const pJson = JSON.stringify({ name: 'something', @@ -47,47 +53,51 @@ describe(getName(), () => { }, }); fs.readLocalFile.mockResolvedValue(pJson); - expect(detectRepoFileConfig()).toMatchSnapshot(); + platform.getJsonFile.mockResolvedValueOnce(pJson); + expect(await detectRepoFileConfig()).toMatchSnapshot(); + expect(await detectRepoFileConfig()).toMatchSnapshot(); }); - it('returns error if cannot parse', () => { + it('returns error if cannot parse', async () => { git.getFileList.mockResolvedValue(['package.json', 'renovate.json']); fs.readLocalFile.mockResolvedValue('cannot parse'); - expect(detectRepoFileConfig()).toMatchSnapshot(); + expect(await detectRepoFileConfig()).toMatchSnapshot(); }); - it('throws error if duplicate keys', () => { + it('throws error if duplicate keys', async () => { git.getFileList.mockResolvedValue(['package.json', '.renovaterc']); fs.readLocalFile.mockResolvedValue( '{ "enabled": true, "enabled": false }' ); - expect(detectRepoFileConfig()).toMatchSnapshot(); + expect(await detectRepoFileConfig()).toMatchSnapshot(); }); - it('finds and parse renovate.json5', () => { + it('finds and parse renovate.json5', async () => { git.getFileList.mockResolvedValue(['package.json', 'renovate.json5']); fs.readLocalFile.mockResolvedValue(`{ // this is json5 format }`); - expect(detectRepoFileConfig()).toMatchSnapshot(); + expect(await detectRepoFileConfig()).toMatchSnapshot(); }); - it('finds .github/renovate.json', () => { + it('finds .github/renovate.json', async () => { git.getFileList.mockResolvedValue([ 'package.json', '.github/renovate.json', ]); fs.readLocalFile.mockResolvedValue('{}'); - expect(detectRepoFileConfig()).toMatchSnapshot(); + expect(await detectRepoFileConfig()).toMatchSnapshot(); }); - it('finds .gitlab/renovate.json', () => { + it('finds .gitlab/renovate.json', async () => { git.getFileList.mockResolvedValue([ 'package.json', '.gitlab/renovate.json', ]); fs.readLocalFile.mockResolvedValue('{}'); - expect(detectRepoFileConfig()).toMatchSnapshot(); + expect(await detectRepoFileConfig()).toMatchSnapshot(); }); - it('finds .renovaterc.json', () => { + it('finds .renovaterc.json', async () => { git.getFileList.mockResolvedValue(['package.json', '.renovaterc.json']); fs.readLocalFile.mockResolvedValue('{}'); - expect(detectRepoFileConfig()).toMatchSnapshot(); + platform.getJsonFile.mockResolvedValueOnce('{"something":"new"}'); + expect(await detectRepoFileConfig()).toMatchSnapshot(); + expect(await detectRepoFileConfig()).toMatchSnapshot(); }); }); describe('checkForRepoConfigError', () => { diff --git a/lib/workers/repository/init/merge.ts b/lib/workers/repository/init/merge.ts index 56bfca3cfe913b..31d2bbffb05453 100644 --- a/lib/workers/repository/init/merge.ts +++ b/lib/workers/repository/init/merge.ts @@ -8,6 +8,7 @@ import { decryptConfig } from '../../../config/decrypt'; import { migrateAndValidate } from '../../../config/migrate-validate'; import { migrateConfig } from '../../../config/migration'; import * as presets from '../../../config/presets'; +import { applySecretsToConfig } from '../../../config/secrets'; import { RenovateConfig } from '../../../config/types'; import { CONFIG_VALIDATION, @@ -15,16 +16,30 @@ import { } from '../../../constants/error-messages'; import * as npmApi from '../../../datasource/npm'; import { logger } from '../../../logger'; +import { platform } from '../../../platform'; +import { getCache } from '../../../util/cache/repository'; import { readLocalFile } from '../../../util/fs'; import { getFileList } from '../../../util/git'; import * as hostRules from '../../../util/host-rules'; import type { RepoFileConfig } from './types'; export async function detectRepoFileConfig(): Promise { + const cache = getCache(); + let { configFileName } = cache; + if (configFileName) { + let configFileParsed = await platform.getJsonFile(configFileName); + if (configFileParsed) { + if (configFileName === 'package.json') { + configFileParsed = configFileParsed.renovate; + } + return { configFileName, configFileParsed }; + } + logger.debug('Existing config file no longer exists'); + } const fileList = await getFileList(); async function detectConfigFile(): Promise { - for (const configFileName of configFileNames) { - if (configFileName === 'package.json') { + for (const fileName of configFileNames) { + if (fileName === 'package.json') { try { const pJson = JSON.parse(await readLocalFile('package.json', 'utf8')); if (pJson.renovate) { @@ -34,17 +49,18 @@ export async function detectRepoFileConfig(): Promise { } catch (err) { // Do nothing } - } else if (fileList.includes(configFileName)) { - return configFileName; + } else if (fileList.includes(fileName)) { + return fileName; } } return null; } - const configFileName = await detectConfigFile(); + configFileName = await detectConfigFile(); if (!configFileName) { logger.debug('No renovate config file found'); return {}; } + cache.configFileName = configFileName; logger.debug(`Found ${configFileName} config file`); let configFileParsed; if (configFileName === 'package.json') { @@ -203,6 +219,10 @@ export async function mergeRenovateConfig( ); npmApi.setNpmrc(resolvedConfig.npmrc); } + resolvedConfig = applySecretsToConfig( + resolvedConfig, + mergeChildConfig(config.secrets || {}, resolvedConfig.secrets || {}) + ); // istanbul ignore if if (resolvedConfig.hostRules) { logger.debug('Setting hostRules from config'); diff --git a/lib/workers/repository/init/semantic.spec.ts b/lib/workers/repository/init/semantic.spec.ts index 1ac0f5a3390d79..e3be88d5c76334 100644 --- a/lib/workers/repository/init/semantic.spec.ts +++ b/lib/workers/repository/init/semantic.spec.ts @@ -1,4 +1,5 @@ import { RenovateConfig, getConfig, getName, git } from '../../../../test/util'; +import { initialize } from '../../../util/cache/repository'; import { detectSemanticCommits } from './semantic'; jest.mock('../../../util/git'); @@ -13,11 +14,20 @@ beforeEach(() => { describe(getName(), () => { describe('detectSemanticCommits()', () => { + beforeEach(async () => { + await initialize({}); + }); it('detects false if unknown', async () => { config.semanticCommits = null; - git.getCommitMessages.mockResolvedValue(['foo', 'bar']); + git.getCommitMessages.mockResolvedValueOnce(['foo', 'bar']); + git.getCommitMessages.mockResolvedValueOnce([ + 'fix: foo', + 'refactor: bar', + ]); const res = await detectSemanticCommits(); expect(res).toBe('disabled'); + const res2 = await detectSemanticCommits(); + expect(res2).toBe('disabled'); }); it('detects true if known', async () => { config.semanticCommits = null; diff --git a/lib/workers/repository/init/semantic.ts b/lib/workers/repository/init/semantic.ts index 62816e63e56341..297fa13a653fdb 100644 --- a/lib/workers/repository/init/semantic.ts +++ b/lib/workers/repository/init/semantic.ts @@ -1,19 +1,26 @@ import conventionalCommitsDetector from 'conventional-commits-detector'; import { logger } from '../../../logger'; +import { getCache } from '../../../util/cache/repository'; import { getCommitMessages } from '../../../util/git'; type DetectedSemanticCommit = 'enabled' | 'disabled'; export async function detectSemanticCommits(): Promise { logger.debug('detectSemanticCommits()'); + const cache = getCache(); + if (cache.semanticCommits) { + return cache.semanticCommits; + } const commitMessages = await getCommitMessages(); logger.trace(`commitMessages=${JSON.stringify(commitMessages)}`); const type = conventionalCommitsDetector(commitMessages); logger.debug('Semantic commits detection: ' + type); if (type === 'angular') { logger.debug('angular semantic commits detected'); - return 'enabled'; + cache.semanticCommits = 'enabled'; + } else { + logger.debug('No semantic commits detected'); + cache.semanticCommits = 'disabled'; } - logger.debug('No semantic commits detected'); - return 'disabled'; + return cache.semanticCommits; } diff --git a/lib/workers/repository/onboarding/branch/check.ts b/lib/workers/repository/onboarding/branch/check.ts index 9a2a4369218454..1c1e2062e1580b 100644 --- a/lib/workers/repository/onboarding/branch/check.ts +++ b/lib/workers/repository/onboarding/branch/check.ts @@ -7,6 +7,7 @@ import { import { logger } from '../../../../logger'; import { platform } from '../../../../platform'; import { PrState } from '../../../../types'; +import { getCache } from '../../../../util/cache/repository'; import { readLocalFile } from '../../../../util/fs'; import { getFileList } from '../../../../util/git'; @@ -57,6 +58,28 @@ export const isOnboarded = async (config: RenovateConfig): Promise => { // Return early and avoid checking for config files return true; } + const cache = getCache(); + if (cache.configFileName) { + logger.debug('Checking cached config file name'); + try { + const configFileContent = await platform.getJsonFile( + cache.configFileName + ); + if (configFileContent) { + if ( + cache.configFileName !== 'package.json' || + configFileContent.renovate + ) { + logger.debug('Existing config file confirmed'); + return true; + } + } + } catch (err) { + // probably file doesn't exist + } + logger.debug('Existing config file no longer exists'); + delete cache.configFileName; + } if (await configFileExists()) { await platform.ensureIssueClosing(title); return true; diff --git a/lib/workers/repository/onboarding/branch/index.spec.ts b/lib/workers/repository/onboarding/branch/index.spec.ts index 10074a91a136f9..43c2991cebd22c 100644 --- a/lib/workers/repository/onboarding/branch/index.spec.ts +++ b/lib/workers/repository/onboarding/branch/index.spec.ts @@ -5,6 +5,7 @@ import { getConfig, getName, git, + mocked, platform, } from '../../../../../test/util'; import { @@ -13,6 +14,7 @@ import { } from '../../../../constants/error-messages'; import { Pr } from '../../../../platform'; import { PrState } from '../../../../types'; +import * as _cache from '../../../../util/cache/repository'; import * as _config from './config'; import * as _rebase from './rebase'; import { checkOnboardingBranch } from '.'; @@ -21,10 +23,13 @@ const rebase: any = _rebase; const configModule: any = _config; jest.mock('../../../../workers/repository/onboarding/branch/rebase'); +jest.mock('../../../../util/cache/repository'); jest.mock('../../../../util/fs'); jest.mock('../../../../util/git'); jest.mock('./config'); +const cache = mocked(_cache); + describe(getName(), () => { describe('checkOnboardingBranch', () => { let config: RenovateConfig; @@ -33,6 +38,7 @@ describe(getName(), () => { config = getConfig(); config.repository = 'some/repo'; git.getFileList.mockResolvedValue([]); + cache.getCache.mockReturnValue({}); }); it('throws if no package files', async () => { await expect(checkOnboardingBranch(config)).rejects.toThrow( @@ -110,6 +116,29 @@ describe(getName(), () => { const res = await checkOnboardingBranch(config); expect(res.repoIsOnboarded).toBe(true); }); + + it('handles removed cached file name', async () => { + cache.getCache.mockReturnValue({ configFileName: '.renovaterc' }); + git.getFileList.mockResolvedValueOnce(['renovate.json']); + const res = await checkOnboardingBranch(config); + expect(res.repoIsOnboarded).toBe(true); + }); + + it('handles cached file name', async () => { + cache.getCache.mockReturnValue({ configFileName: '.renovaterc' }); + platform.getJsonFile.mockResolvedValueOnce({}); + const res = await checkOnboardingBranch(config); + expect(res.repoIsOnboarded).toBe(true); + }); + + it('handles cached package.json', async () => { + cache.getCache.mockReturnValue({ configFileName: 'package.json' }); + platform.getJsonFile.mockResolvedValueOnce({ renovate: {} }); + fs.readLocalFile.mockResolvedValueOnce('{}'); + const res = await checkOnboardingBranch(config); + expect(res.repoIsOnboarded).toBe(true); + }); + it('detects repo is onboarded via package.json config', async () => { git.getFileList.mockResolvedValueOnce(['package.json']); fs.readLocalFile.mockResolvedValueOnce('{"renovate":{}}'); diff --git a/lib/workers/repository/onboarding/pr/__snapshots__/errors-warnings.spec.ts.snap b/lib/workers/repository/onboarding/pr/__snapshots__/errors-warnings.spec.ts.snap index ba787dda8c7639..0262a1986aaba4 100644 --- a/lib/workers/repository/onboarding/pr/__snapshots__/errors-warnings.spec.ts.snap +++ b/lib/workers/repository/onboarding/pr/__snapshots__/errors-warnings.spec.ts.snap @@ -4,7 +4,7 @@ exports[`workers/repository/onboarding/pr/errors-warnings getDepWarnings() retur " --- -### ⚠ī¸ Dependency Lookup Warnings ⚠ī¸ +### ⚠ Dependency Lookup Warnings ⚠ Please correct - or verify that you can safely ignore - these lookup failures before you merge this PR. diff --git a/lib/workers/repository/process/index.ts b/lib/workers/repository/process/index.ts index 008d948bc9ea48..58a9e98541d257 100644 --- a/lib/workers/repository/process/index.ts +++ b/lib/workers/repository/process/index.ts @@ -2,10 +2,10 @@ import { mergeChildConfig } from '../../../config'; import type { RenovateConfig } from '../../../config/types'; import { logger } from '../../../logger'; import type { PackageFile } from '../../../manager/types'; -import { platform } from '../../../platform'; import { branchExists } from '../../../util/git'; import { addSplit } from '../../../util/split'; import type { BranchConfig } from '../../types'; +import { readDashboardBody } from '../dependency-dashboard'; import { ExtractResult, extract, lookup, update } from './extract-update'; import type { WriteUpdateResult } from './write'; @@ -25,38 +25,7 @@ function getBaseBranchConfig( export async function extractDependencies( config: RenovateConfig ): Promise { - logger.debug('processRepo()'); - /* eslint-disable no-param-reassign */ - config.dependencyDashboardChecks = {}; - const stringifiedConfig = JSON.stringify(config); - // istanbul ignore next - if ( - config.dependencyDashboard || - stringifiedConfig.includes('"dependencyDashboardApproval":true') || - stringifiedConfig.includes('"prCreation":"approval"') - ) { - config.dependencyDashboardTitle = - config.dependencyDashboardTitle || `Dependency Dashboard`; - const issue = await platform.findIssue(config.dependencyDashboardTitle); - if (issue) { - const checkMatch = ' - \\[x\\] '; - const checked = issue.body.match(new RegExp(checkMatch, 'g')); - if (checked?.length) { - const re = new RegExp(checkMatch); - checked.forEach((check) => { - const [, type, branchName] = re.exec(check); - config.dependencyDashboardChecks[branchName] = type; - }); - } - const checkedRebaseAll = issue.body.includes( - ' - [x] ' - ); - if (checkedRebaseAll) { - config.dependencyDashboardRebaseAllOpen = true; - /* eslint-enable no-param-reassign */ - } - } - } + await readDashboardBody(config); let res: ExtractResult = { branches: [], branchList: [], diff --git a/lib/workers/repository/process/lookup/__snapshots__/filter-checks.spec.ts.snap b/lib/workers/repository/process/lookup/__snapshots__/filter-checks.spec.ts.snap index f272e20082924a..045360b8b2e754 100644 --- a/lib/workers/repository/process/lookup/__snapshots__/filter-checks.spec.ts.snap +++ b/lib/workers/repository/process/lookup/__snapshots__/filter-checks.spec.ts.snap @@ -2,7 +2,7 @@ exports[`workers/repository/process/lookup/filter-checks .filterInternalChecks() picks up stabilityDays settings from hostRules 1`] = ` Object { - "pendingChecks": Array [], + "pendingChecks": false, "pendingReleases": Array [], "release": Object { "releaseTimestamp": "2021-01-07T00:00:00.000Z", @@ -13,7 +13,7 @@ Object { exports[`workers/repository/process/lookup/filter-checks .filterInternalChecks() picks up stabilityDays settings from updateType 1`] = ` Object { - "pendingChecks": Array [], + "pendingChecks": false, "pendingReleases": Array [ Object { "releaseTimestamp": "2021-01-07T00:00:00.000Z", @@ -29,7 +29,7 @@ Object { exports[`workers/repository/process/lookup/filter-checks .filterInternalChecks() returns latest release if internalChecksFilter=none 1`] = ` Object { - "pendingChecks": Array [], + "pendingChecks": false, "pendingReleases": Array [], "release": Object { "releaseTimestamp": "2021-01-07T00:00:00.000Z", @@ -40,7 +40,7 @@ Object { exports[`workers/repository/process/lookup/filter-checks .filterInternalChecks() returns non-latest release if internalChecksFilter=flexible and some pass checks 1`] = ` Object { - "pendingChecks": Array [], + "pendingChecks": false, "pendingReleases": Array [ Object { "releaseTimestamp": "2021-01-05T00:00:00.000Z", @@ -60,7 +60,7 @@ Object { exports[`workers/repository/process/lookup/filter-checks .filterInternalChecks() returns non-latest release if internalChecksFilter=strict and some pass checks 1`] = ` Object { - "pendingChecks": Array [], + "pendingChecks": false, "pendingReleases": Array [ Object { "releaseTimestamp": "2021-01-05T00:00:00.000Z", @@ -80,7 +80,7 @@ Object { exports[`workers/repository/process/lookup/filter-checks .filterInternalChecks() returns non-pending latest release if internalChecksFilter=flexible and none pass checks 1`] = ` Object { - "pendingChecks": Array [], + "pendingChecks": false, "pendingReleases": Array [], "release": Object { "releaseTimestamp": "2021-01-07T00:00:00.000Z", @@ -91,9 +91,7 @@ Object { exports[`workers/repository/process/lookup/filter-checks .filterInternalChecks() returns pending latest release if internalChecksFilter=strict and none pass checks 1`] = ` Object { - "pendingChecks": Array [ - "stabilityDays", - ], + "pendingChecks": true, "pendingReleases": Array [], "release": Object { "releaseTimestamp": "2021-01-07T00:00:00.000Z", diff --git a/lib/workers/repository/process/lookup/filter-checks.spec.ts b/lib/workers/repository/process/lookup/filter-checks.spec.ts index 3299a12d68c609..7ba7c0bb026211 100644 --- a/lib/workers/repository/process/lookup/filter-checks.spec.ts +++ b/lib/workers/repository/process/lookup/filter-checks.spec.ts @@ -55,7 +55,7 @@ describe(getName(), () => { sortedReleases ); expect(res).toMatchSnapshot(); - expect(res.pendingChecks).toHaveLength(0); + expect(res.pendingChecks).toBe(false); expect(res.pendingReleases).toHaveLength(0); expect(res.release.version).toEqual('1.0.4'); }); @@ -70,7 +70,7 @@ describe(getName(), () => { sortedReleases ); expect(res).toMatchSnapshot(); - expect(res.pendingChecks).toHaveLength(0); + expect(res.pendingChecks).toBe(false); expect(res.pendingReleases).toHaveLength(0); expect(res.release.version).toEqual('1.0.4'); }); @@ -85,7 +85,7 @@ describe(getName(), () => { sortedReleases ); expect(res).toMatchSnapshot(); - expect(res.pendingChecks).toHaveLength(1); + expect(res.pendingChecks).toBe(true); expect(res.pendingReleases).toHaveLength(0); expect(res.release.version).toEqual('1.0.4'); }); @@ -100,7 +100,7 @@ describe(getName(), () => { sortedReleases ); expect(res).toMatchSnapshot(); - expect(res.pendingChecks).toHaveLength(0); + expect(res.pendingChecks).toBe(false); expect(res.pendingReleases).toHaveLength(2); expect(res.release.version).toEqual('1.0.2'); }); @@ -115,7 +115,7 @@ describe(getName(), () => { sortedReleases ); expect(res).toMatchSnapshot(); - expect(res.pendingChecks).toHaveLength(0); + expect(res.pendingChecks).toBe(false); expect(res.pendingReleases).toHaveLength(2); expect(res.release.version).toEqual('1.0.2'); }); @@ -131,7 +131,7 @@ describe(getName(), () => { sortedReleases ); expect(res).toMatchSnapshot(); - expect(res.pendingChecks).toHaveLength(0); + expect(res.pendingChecks).toBe(false); expect(res.pendingReleases).toHaveLength(0); expect(res.release.version).toEqual('1.0.4'); }); @@ -146,7 +146,7 @@ describe(getName(), () => { sortedReleases ); expect(res).toMatchSnapshot(); - expect(res.pendingChecks).toHaveLength(0); + expect(res.pendingChecks).toBe(false); expect(res.pendingReleases).toHaveLength(1); expect(res.release.version).toEqual('1.0.3'); }); diff --git a/lib/workers/repository/process/lookup/filter-checks.ts b/lib/workers/repository/process/lookup/filter-checks.ts index 0b0176957f03a1..f93d7e32e8feff 100644 --- a/lib/workers/repository/process/lookup/filter-checks.ts +++ b/lib/workers/repository/process/lookup/filter-checks.ts @@ -10,7 +10,7 @@ import { getUpdateType } from './update-type'; export interface InternalChecksResult { release: Release; - pendingChecks?: string[]; + pendingChecks: boolean; pendingReleases?: Release[]; } @@ -22,7 +22,7 @@ export function filterInternalChecks( ): InternalChecksResult { const { currentVersion, depName, internalChecksFilter } = config; let release: Release; - const pendingChecks: string[] = []; + let pendingChecks = false; let pendingReleases: Release[] = []; if (internalChecksFilter === 'none') { // Don't care if stabilityDays are unmet @@ -73,7 +73,7 @@ export function filterInternalChecks( // None are pending anymore because we took the latest, so empty the array pendingReleases = []; if (internalChecksFilter === 'strict') { - pendingChecks.push('stabilityDays'); + pendingChecks = true; } } } diff --git a/lib/workers/repository/process/lookup/index.spec.ts b/lib/workers/repository/process/lookup/index.spec.ts index 59aabd5f1ad65f..d1839fba200b68 100644 --- a/lib/workers/repository/process/lookup/index.spec.ts +++ b/lib/workers/repository/process/lookup/index.spec.ts @@ -1,4 +1,4 @@ -import nock from 'nock'; +import * as httpMock from '../../../../../test/http-mock'; import { getConfig, getName, @@ -59,6 +59,9 @@ describe(getName(), () => { jest.resetAllMocks(); }); + // TODO: fix mocks + afterEach(() => httpMock.clear(false)); + describe('.lookupUpdates()', () => { it('returns null if unknown datasource', async () => { config.depName = 'some-dep'; @@ -70,7 +73,7 @@ describe(getName(), () => { config.depName = 'q'; config.datasource = datasourceNpmId; config.rollbackPrs = true; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('returns rollback for ranged version', async () => { @@ -78,7 +81,7 @@ describe(getName(), () => { config.depName = 'q'; config.datasource = datasourceNpmId; config.rollbackPrs = true; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('supports minor and major upgrades for tilde ranges', async () => { @@ -86,7 +89,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('supports lock file updates mixed with regular updates', async () => { @@ -96,7 +99,7 @@ describe(getName(), () => { config.datasource = datasourceNpmId; config.separateMinorPatch = true; config.lockedVersion = '0.4.0'; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('returns multiple updates if grouping but separateMajorMinor=true', async () => { @@ -105,7 +108,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); expect(res.updates).toHaveLength(2); @@ -117,7 +120,7 @@ describe(getName(), () => { config.depName = 'q'; config.separateMinorPatch = true; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); expect(res.updates).toHaveLength(3); @@ -129,7 +132,7 @@ describe(getName(), () => { config.separateMajorMinor = false; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); expect(res.updates).toHaveLength(1); @@ -140,7 +143,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('enforces allowedVersions', async () => { @@ -148,7 +151,7 @@ describe(getName(), () => { config.allowedVersions = '<1'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toHaveLength(1); }); it('enforces allowedVersions with regex', async () => { @@ -156,7 +159,7 @@ describe(getName(), () => { config.allowedVersions = '/^0/'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toHaveLength(1); }); it('enforces allowedVersions with negative regex', async () => { @@ -164,7 +167,7 @@ describe(getName(), () => { config.allowedVersions = '!/^1/'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toHaveLength(1); }); it('falls back to semver syntax allowedVersions', async () => { @@ -173,7 +176,7 @@ describe(getName(), () => { config.depName = 'q'; config.versioning = dockerVersioningId; // this doesn't make sense but works for this test config.datasource = datasourceNpmId; // this doesn't make sense but works for this test - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toHaveLength(1); }); it('falls back to pep440 syntax allowedVersions', async () => { @@ -182,7 +185,7 @@ describe(getName(), () => { config.depName = 'q'; config.versioning = poetryVersioningId; // this doesn't make sense but works for this test config.datasource = datasourceNpmId; // this doesn't make sense but works for this test - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toHaveLength(1); }); it('skips invalid allowedVersions', async () => { @@ -190,7 +193,7 @@ describe(getName(), () => { config.allowedVersions = 'less than 1'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); await expect(lookup.lookupUpdates(config)).rejects.toThrow( Error(CONFIG_VALIDATION) ); @@ -200,7 +203,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); expect(res.updates).toHaveLength(2); @@ -218,7 +221,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); expect(res.updates[0].updateType).toEqual('patch'); @@ -229,7 +232,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('returns patch minor and major', async () => { @@ -238,7 +241,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toHaveLength(3); expect(res.updates).toMatchSnapshot(); @@ -249,7 +252,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('disables major release separation (minor)', async () => { @@ -258,7 +261,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('uses minimum version for vulnerabilityAlerts', async () => { @@ -266,7 +269,7 @@ describe(getName(), () => { config.isVulnerabilityAlert = true; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = (await lookup.lookupUpdates(config)).updates; expect(res).toMatchSnapshot(); expect(res).toHaveLength(1); @@ -276,7 +279,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('ignores pinning for ranges when other upgrade exists', async () => { @@ -284,7 +287,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades minor ranged versions', async () => { @@ -292,7 +295,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('handles update-lockfile', async () => { @@ -301,7 +304,7 @@ describe(getName(), () => { config.rangeStrategy = 'update-lockfile'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); expect(res.updates[0].updateType).toEqual('minor'); @@ -311,7 +314,7 @@ describe(getName(), () => { config.rangeStrategy = 'widen'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('replaces minor complex ranged versions if configured', async () => { @@ -319,7 +322,7 @@ describe(getName(), () => { config.rangeStrategy = 'replace'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('widens major ranged versions if configured', async () => { @@ -327,7 +330,8 @@ describe(getName(), () => { config.rangeStrategy = 'widen'; config.depName = 'webpack'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/webpack') .reply(200, webpackJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); @@ -337,7 +341,8 @@ describe(getName(), () => { config.rangeStrategy = 'replace'; config.depName = 'webpack'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/webpack') .reply(200, webpackJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); @@ -347,7 +352,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('uses the locked version for pinning', async () => { @@ -356,7 +361,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('ignores minor ranged versions when not pinning', async () => { @@ -364,7 +369,7 @@ describe(getName(), () => { config.currentValue = '^1.0.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toHaveLength(0); }); it('ignores minor ranged versions when locked', async () => { @@ -373,7 +378,7 @@ describe(getName(), () => { config.lockedVersion = '1.1.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toHaveLength(0); }); it('upgrades tilde ranges', async () => { @@ -381,7 +386,7 @@ describe(getName(), () => { config.currentValue = '~1.3.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades .x minor ranges', async () => { @@ -389,7 +394,7 @@ describe(getName(), () => { config.rangeStrategy = 'pin'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades tilde ranges without pinning', async () => { @@ -397,7 +402,7 @@ describe(getName(), () => { config.currentValue = '~1.3.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades .x major ranges without pinning', async () => { @@ -405,7 +410,7 @@ describe(getName(), () => { config.currentValue = '0.x'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades .x minor ranges without pinning', async () => { @@ -413,7 +418,7 @@ describe(getName(), () => { config.currentValue = '1.3.x'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades .x complex minor ranges without pinning', async () => { @@ -421,7 +426,7 @@ describe(getName(), () => { config.currentValue = '1.2.x - 1.3.x'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades shorthand major ranges without pinning', async () => { @@ -429,7 +434,7 @@ describe(getName(), () => { config.currentValue = '0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades shorthand minor ranges without pinning', async () => { @@ -437,7 +442,7 @@ describe(getName(), () => { config.currentValue = '1.3'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades multiple tilde ranges without pinning', async () => { @@ -445,7 +450,7 @@ describe(getName(), () => { config.currentValue = '~0.7.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades multiple caret ranges without pinning', async () => { @@ -453,7 +458,7 @@ describe(getName(), () => { config.currentValue = '^0.7.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('supports complex ranges', async () => { @@ -461,7 +466,7 @@ describe(getName(), () => { config.currentValue = '^0.7.0 || ^0.8.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toHaveLength(2); expect(res.updates[0]).toMatchSnapshot(); @@ -471,7 +476,8 @@ describe(getName(), () => { config.currentValue = '^1.0.0 || ^2.0.0'; config.depName = 'webpack'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/webpack') .reply(200, webpackJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); @@ -481,7 +487,8 @@ describe(getName(), () => { config.currentValue = '1.x - 2.x'; config.depName = 'webpack'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/webpack') .reply(200, webpackJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); @@ -491,7 +498,8 @@ describe(getName(), () => { config.currentValue = '1.x || 2.x'; config.depName = 'webpack'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/webpack') .reply(200, webpackJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); @@ -501,7 +509,8 @@ describe(getName(), () => { config.currentValue = '1 || 2'; config.depName = 'webpack'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/webpack') .reply(200, webpackJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); @@ -511,7 +520,7 @@ describe(getName(), () => { config.currentValue = '~1.2.0 || ~1.3.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('returns nothing for greater than ranges', async () => { @@ -519,7 +528,7 @@ describe(getName(), () => { config.currentValue = '>= 0.7.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toHaveLength(0); }); it('upgrades less than equal ranges without pinning', async () => { @@ -527,7 +536,7 @@ describe(getName(), () => { config.currentValue = '<= 0.7.2'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades less than ranges without pinning', async () => { @@ -535,7 +544,7 @@ describe(getName(), () => { config.currentValue = '< 0.7.2'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades less than major ranges', async () => { @@ -543,7 +552,7 @@ describe(getName(), () => { config.currentValue = '< 1'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades less than equal minor ranges', async () => { @@ -551,7 +560,7 @@ describe(getName(), () => { config.currentValue = '<= 1.3'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades equal minor ranges', async () => { @@ -559,7 +568,7 @@ describe(getName(), () => { config.currentValue = '=1.3.1'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades less than equal major ranges', async () => { @@ -568,7 +577,7 @@ describe(getName(), () => { config.currentValue = '<= 1'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('upgrades major less than equal ranges', async () => { @@ -576,7 +585,7 @@ describe(getName(), () => { config.currentValue = '<= 1.0.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); expect(res.updates[0].newValue).toEqual('<= 1.4.1'); @@ -586,7 +595,7 @@ describe(getName(), () => { config.currentValue = '< 1.0.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); expect(res.updates[0].newValue).toEqual('< 2.0.0'); @@ -596,7 +605,7 @@ describe(getName(), () => { config.currentValue = '>= 0.5.0 < 1.0.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); expect(res.updates[0].newValue).toEqual('>= 0.5.0 < 2.0.0'); @@ -606,7 +615,7 @@ describe(getName(), () => { config.currentValue = '>= 0.5.0 <0.8'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); expect(res.updates[0].newValue).toEqual('>= 0.5.0 <0.10'); @@ -617,7 +626,7 @@ describe(getName(), () => { config.currentValue = '>= 0.5.0 <= 0.8.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); expect(res.updates[0].newValue).toEqual('>= 0.5.0 <= 0.9.7'); @@ -628,7 +637,7 @@ describe(getName(), () => { config.currentValue = '<= 0.8.0 >= 0.5.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); }); @@ -637,14 +646,17 @@ describe(getName(), () => { config.currentValue = '1.4.1'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('should ignore unstable versions if the current version is stable', async () => { config.currentValue = '2.5.16'; config.depName = 'vue'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/vue').reply(200, vueJson); + httpMock + .scope('https://registry.npmjs.org') + .get('/vue') + .reply(200, vueJson); expect((await lookup.lookupUpdates(config)).updates).toHaveLength(0); }); it('should ignore unstable versions from datasource', async () => { @@ -696,7 +708,7 @@ describe(getName(), () => { const res = await lookup.lookupUpdates(config); expect(res.updates).toHaveLength(1); expect(res.updates[0].newVersion).toEqual('1.4.6'); - expect(res.updates[0].pendingChecks).toHaveLength(1); + expect(res.updates[0].pendingChecks).toBe(true); }); it('should return pendingVersions', async () => { @@ -736,7 +748,10 @@ describe(getName(), () => { config.respectLatest = false; config.depName = 'vue'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/vue').reply(200, vueJson); + httpMock + .scope('https://registry.npmjs.org') + .get('/vue') + .reply(200, vueJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); expect(res.updates).toHaveLength(1); @@ -746,7 +761,8 @@ describe(getName(), () => { config.currentValue = '3.1.0-dev.20180731'; config.depName = 'typescript'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/typescript') .reply(200, typescriptJson); const res = await lookup.lookupUpdates(config); @@ -758,7 +774,8 @@ describe(getName(), () => { config.currentValue = '3.0.1-insiders.20180726'; config.depName = 'typescript'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/typescript') .reply(200, typescriptJson); const res = await lookup.lookupUpdates(config); @@ -771,7 +788,8 @@ describe(getName(), () => { config.depName = 'typescript'; config.datasource = datasourceNpmId; config.followTag = 'insiders'; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/typescript') .reply(200, typescriptJson); const res = await lookup.lookupUpdates(config); @@ -785,7 +803,8 @@ describe(getName(), () => { config.datasource = datasourceNpmId; config.followTag = 'insiders'; config.rollbackPrs = true; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/typescript') .reply(200, typescriptJson); const res = await lookup.lookupUpdates(config); @@ -798,7 +817,8 @@ describe(getName(), () => { config.depName = 'typescript'; config.datasource = datasourceNpmId; config.followTag = 'insiders'; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/typescript') .reply(200, typescriptJson); const res = await lookup.lookupUpdates(config); @@ -811,7 +831,8 @@ describe(getName(), () => { config.depName = 'typescript'; config.datasource = datasourceNpmId; config.followTag = 'insiders'; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/typescript') .reply(200, typescriptJson); const res = await lookup.lookupUpdates(config); @@ -822,7 +843,8 @@ describe(getName(), () => { config.depName = 'typescript'; config.datasource = datasourceNpmId; config.followTag = 'foo'; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/typescript') .reply(200, typescriptJson); const res = await lookup.lookupUpdates(config); @@ -838,7 +860,8 @@ describe(getName(), () => { config.currentValue = '~0.0.34'; config.depName = '@types/helmet'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/@types%2Fhelmet') .reply(200, helmetJson); expect((await lookup.lookupUpdates(config)).updates).toEqual([]); @@ -848,7 +871,8 @@ describe(getName(), () => { config.currentValue = '^0.0.34'; config.depName = '@types/helmet'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/@types%2Fhelmet') .reply(200, helmetJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); @@ -858,7 +882,8 @@ describe(getName(), () => { config.depName = 'coffeelint'; config.datasource = datasourceNpmId; config.rollbackPrs = true; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/coffeelint') .reply(200, coffeelintJson); const res = await lookup.lookupUpdates(config); @@ -869,7 +894,8 @@ describe(getName(), () => { config.currentValue = '1.0.0'; config.depName = 'webpack'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/webpack') .reply(200, webpackJson); const res = await lookup.lookupUpdates(config); @@ -880,7 +906,8 @@ describe(getName(), () => { config.separateMultipleMajor = true; config.depName = 'webpack'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org') + httpMock + .scope('https://registry.npmjs.org') .get('/webpack') .reply(200, webpackJson); const res = await lookup.lookupUpdates(config); @@ -891,7 +918,10 @@ describe(getName(), () => { config.rangeStrategy = 'replace'; config.depName = 'next'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/next').reply(200, nextJson); + httpMock + .scope('https://registry.npmjs.org') + .get('/next') + .reply(200, nextJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toHaveLength(0); }); @@ -900,7 +930,7 @@ describe(getName(), () => { config.currentValue = '^1.0.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('supports in-range tilde updates', async () => { @@ -909,7 +939,7 @@ describe(getName(), () => { config.depName = 'q'; config.separateMinorPatch = true; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('supports in-range tilde patch updates', async () => { @@ -918,7 +948,7 @@ describe(getName(), () => { config.depName = 'q'; config.separateMinorPatch = true; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('supports in-range gte updates', async () => { @@ -926,7 +956,7 @@ describe(getName(), () => { config.currentValue = '>=1.0.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('supports majorgte updates', async () => { @@ -935,7 +965,7 @@ describe(getName(), () => { config.depName = 'q'; config.datasource = datasourceNpmId; config.separateMajorMinor = false; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('rejects in-range unsupported operator', async () => { @@ -943,7 +973,7 @@ describe(getName(), () => { config.currentValue = '>1.0.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('rejects non-fully specified in-range updates', async () => { @@ -951,7 +981,7 @@ describe(getName(), () => { config.currentValue = '1.x'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('rejects complex range in-range updates', async () => { @@ -959,7 +989,7 @@ describe(getName(), () => { config.currentValue = '^0.9.0 || ^1.0.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('replaces non-range in-range updates', async () => { @@ -968,7 +998,7 @@ describe(getName(), () => { config.packageFile = 'package.json'; config.rangeStrategy = 'bump'; config.currentValue = '1.0.0'; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('handles github 404', async () => { @@ -976,7 +1006,7 @@ describe(getName(), () => { config.datasource = datasourceGithubTagsId; config.packageFile = 'package.json'; config.currentValue = '1.0.0'; - nock('https://pypi.org').get('/pypi/foo/json').reply(404); + httpMock.scope('https://pypi.org').get('/pypi/foo/json').reply(404); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('handles pypi 404', async () => { @@ -984,7 +1014,8 @@ describe(getName(), () => { config.datasource = datasourcePypiId; config.packageFile = 'requirements.txt'; config.currentValue = '1.0.0'; - nock('https://api.github.com') + httpMock + .scope('https://api.github.com') .get('/repos/some/repo/git/refs/tags?per_page=100') .reply(404); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); @@ -995,7 +1026,10 @@ describe(getName(), () => { config.packageFile = 'composer.json'; config.currentValue = '1.0.0'; config.registryUrls = ['https://packagist.org']; - nock('https://packagist.org').get('/packages/foo/bar.json').reply(404); + httpMock + .scope('https://packagist.org') + .get('/packages/foo/bar.json') + .reply(404); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); }); it('handles unknown datasource', async () => { @@ -1016,7 +1050,7 @@ describe(getName(), () => { config.depName = 'q'; // TODO: we are using npm as source to test pep440 (#9721) config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res.updates).toMatchSnapshot(); }); @@ -1024,7 +1058,7 @@ describe(getName(), () => { config.currentValue = '1.3.0'; config.depName = 'q'; config.datasource = datasourceNpmId; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res).toMatchSnapshot(); expect(res.sourceUrl).toBeDefined(); @@ -1036,7 +1070,10 @@ describe(getName(), () => { const returnJson = JSON.parse(JSON.stringify(qJson)); returnJson.name = 'q2'; returnJson.versions['1.4.1'].deprecated = 'true'; - nock('https://registry.npmjs.org').get('/q2').reply(200, returnJson); + httpMock + .scope('https://registry.npmjs.org') + .get('/q2') + .reply(200, returnJson); const res = await lookup.lookupUpdates(config); expect(res).toMatchSnapshot(); expect(res.updates[0].newVersion).toEqual('1.4.0'); @@ -1052,7 +1089,10 @@ describe(getName(), () => { repository: { url: null, directory: 'test' }, }; - nock('https://registry.npmjs.org').get('/q3').reply(200, returnJson); + httpMock + .scope('https://registry.npmjs.org') + .get('/q3') + .reply(200, returnJson); const res = await lookup.lookupUpdates(config); expect(res).toMatchSnapshot(); expect(res.updates[0].newVersion).toEqual('1.4.1'); @@ -1248,7 +1288,7 @@ describe(getName(), () => { allowedVersions: '< 1.4.0', }, ]; - nock('https://registry.npmjs.org').get('/q').reply(200, qJson); + httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson); const res = await lookup.lookupUpdates(config); expect(res).toMatchSnapshot(); }); diff --git a/lib/workers/repository/process/lookup/index.ts b/lib/workers/repository/process/lookup/index.ts index 3168c166d126eb..9f5f226bf6ee18 100644 --- a/lib/workers/repository/process/lookup/index.ts +++ b/lib/workers/repository/process/lookup/index.ts @@ -231,7 +231,7 @@ export async function lookupUpdates( bucket, release ); - if (pendingChecks.length) { + if (pendingChecks) { update.pendingChecks = pendingChecks; } if (pendingReleases.length) { diff --git a/lib/workers/repository/process/write.ts b/lib/workers/repository/process/write.ts index 1fa41c3c698bbe..2dfac7b13eea06 100644 --- a/lib/workers/repository/process/write.ts +++ b/lib/workers/repository/process/write.ts @@ -36,6 +36,8 @@ export async function writeUpdates( addMeta({ branch: branch.branchName }); const branchExisted = branchExists(branch.branchName); const res = await processBranch(branch); + branch.prBlockedBy = res?.prBlockedBy; + branch.prNo = res?.prNo; branch.result = res?.result; if ( branch.result === BranchResult.Automerged && diff --git a/lib/workers/repository/updates/generate.spec.ts b/lib/workers/repository/updates/generate.spec.ts index 987ab18b7f0a2d..8c691358ae95c3 100644 --- a/lib/workers/repository/updates/generate.spec.ts +++ b/lib/workers/repository/updates/generate.spec.ts @@ -553,18 +553,18 @@ describe(getName(), () => { groupName: 'some-group', branchName: 'some-branch', prTitle: 'some-title', - pendingChecks: ['check'], + pendingChecks: true, }, { depName: 'some-dep', groupName: 'some-group', branchName: 'some-branch', prTitle: 'some-title', - pendingChecks: ['check'], + pendingChecks: true, }, ]; const res = generateBranchConfig(branch); - expect(res.pendingChecks).toHaveLength(1); + expect(res.pendingChecks).toBe(true); expect(res.upgrades).toHaveLength(2); }); it('filters pendingChecks', () => { @@ -574,7 +574,7 @@ describe(getName(), () => { groupName: 'some-group', branchName: 'some-branch', prTitle: 'some-title', - pendingChecks: ['check'], + pendingChecks: true, }, { depName: 'some-dep', diff --git a/lib/workers/types.ts b/lib/workers/types.ts index 5e9fca00d498b1..248d451352a71f 100644 --- a/lib/workers/types.ts +++ b/lib/workers/types.ts @@ -66,18 +66,12 @@ export interface BranchUpgradeConfig sourceUrl?: string; } -export enum PrResult { - AwaitingApproval = 'AwaitingApproval', - AwaitingGreenBranch = 'AwaitingGreenBranch', - AwaitingNotPending = 'AwaitingNotPending', - BlockedByBranchAutomerge = 'BlockedByBranchAutomerge', - Created = 'Created', - Error = 'Error', - ErrorAlreadyExists = 'ErrorAlreadyExists', - NotUpdated = 'NotUpdated', - Updated = 'Updated', - LimitReached = 'LimitReached', -} +export type PrBlockedBy = + | 'BranchAutomerge' + | 'NeedsApproval' + | 'AwaitingTests' + | 'RateLimited' + | 'Error'; export enum BranchResult { AlreadyExisted = 'already-existed', @@ -95,6 +89,7 @@ export enum BranchResult { CommitLimitReached = 'commit-limit-reached', BranchLimitReached = 'branch-limit-reached', Rebase = 'rebase', + UpdateNotScheduled = 'update-not-scheduled', } export interface BranchConfig @@ -113,4 +108,6 @@ export interface BranchConfig result?: BranchResult; upgrades: BranchUpgradeConfig[]; packageFiles?: Record; + prBlockedBy?: PrBlockedBy; + prNo?: number; } diff --git a/package.json b/package.json index 1feecfa1cbdccb..75f5410404de58 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,9 @@ "eslint-fix": "eslint --ext .js,.mjs,.ts --fix lib/ test/ tools/", "generate": "run-s generate:*", "generate:imports": "node tools/generate-imports.mjs", - "jest": "cross-env NODE_ENV=test LOG_LEVEL=fatal node --expose-gc node_modules/jest/bin/jest.js", - "jest-debug": "cross-env NODE_ENV=test LOG_LEVEL=fatal node --expose-gc --inspect-brk node_modules/jest/bin/jest.js", - "jest-silent": "cross-env NODE_ENV=test yarn jest --reporters jest-silent-reporter", + "jest": "cross-env NODE_ENV=test LOG_LEVEL=fatal node --expose-gc node_modules/jest/bin/jest.js --logHeapUsage", + "jest-debug": "cross-env NODE_OPTIONS=--inspect-brk yarn jest", + "jest-silent": "cross-env yarn jest --reporters jest-silent-reporter", "lint": "run-s ls-lint eslint prettier markdown-lint", "lint-fix": "run-s eslint-fix prettier-fix markdown-lint-fix", "ls-lint": "ls-lint", @@ -132,7 +132,7 @@ "@yarnpkg/parsers": "2.3.0", "azure-devops-node-api": "10.2.2", "bunyan": "1.8.15", - "cacache": "15.1.0", + "cacache": "15.2.0", "chalk": "4.1.1", "changelog-filename-regex": "2.0.1", "clean-git-ref": "2.0.1", @@ -142,8 +142,12 @@ "deepmerge": "4.2.2", "delay": "5.0.0", "dequal": "2.0.2", - "detect-indent": "6.0.0", + "detect-indent": "6.1.0", "email-addresses": "4.0.0", + "extract-zip": "2.0.1", + "emoji-regex": "9.2.2", + "emojibase": "5.2.0", + "emojibase-regex": "5.1.3", "fast-safe-stringify": "2.0.7", "find-up": "5.0.0", "fs-extra": "10.0.0", @@ -165,8 +169,7 @@ "markdown-table": "2.0.0", "minimatch": "3.0.4", "moo": "0.5.1", - "node-emoji": "1.10.0", - "node-html-parser": "3.3.0", + "node-html-parser": "3.3.5", "p-all": "3.0.0", "p-map": "4.0.0", "p-queue": "6.6.2", @@ -179,9 +182,9 @@ "semver": "7.3.5", "semver-stable": "3.0.0", "semver-utils": "1.1.4", - "shlex": "2.0.2", + "shlex": "2.1.0", "shortid": "2.2.16", - "simple-git": "2.39.0", + "simple-git": "2.39.1", "slugify": "1.5.3", "traverse": "0.6.6", "upath": "2.0.1", @@ -194,10 +197,10 @@ "re2": "1.16.0" }, "devDependencies": { - "@actions/core": "1.2.7", - "@jest/globals": "26.6.2", - "@jest/reporters": "26.6.2", - "@jest/test-result": "26.6.2", + "@actions/core": "1.4.0", + "@jest/globals": "27.0.3", + "@jest/reporters": "27.0.4", + "@jest/test-result": "27.0.2", "@ls-lint/ls-lint": "1.9.2", "@semantic-release/exec": "5.0.0", "@types/bunyan": "1.8.6", @@ -205,7 +208,7 @@ "@types/changelog-filename-regex": "2.0.0", "@types/clean-git-ref": "2.0.0", "@types/conventional-commits-detector": "1.0.0", - "@types/eslint": "7.2.10", + "@types/eslint": "7.2.13", "@types/fs-extra": "9.0.11", "@types/git-url-parse": "9.0.0", "@types/github-url-from-git": "1.5.0", @@ -215,12 +218,12 @@ "@types/js-yaml": "4.0.1", "@types/json-dup-key-validator": "1.0.0", "@types/linkify-markdown": "1.0.0", - "@types/luxon": "1.26.5", - "@types/markdown-it": "12.0.1", + "@types/luxon": "1.27.0", + "@types/markdown-it": "12.0.2", "@types/markdown-table": "2.0.0", "@types/moo": "0.5.4", "@types/nock": "10.0.3", - "@types/node": "14.17.0", + "@types/node": "14.17.3", "@types/node-emoji": "1.8.1", "@types/parse-link-header": "1.0.0", "@types/registry-auth-token": "4.2.0", @@ -231,50 +234,46 @@ "@types/traverse": "0.6.32", "@types/url-join": "4.0.0", "@types/xmldoc": "1.1.5", - "@typescript-eslint/eslint-plugin": "4.23.0", - "@typescript-eslint/parser": "4.23.0", + "@typescript-eslint/eslint-plugin": "4.26.1", + "@typescript-eslint/parser": "4.26.1", "conventional-changelog-conventionalcommits": "4.6.0", "cross-env": "7.0.3", - "eslint": "7.26.0", + "emojibase-data": "6.2.0", + "eslint": "7.28.0", "eslint-config-airbnb-typescript": "12.3.1", "eslint-config-prettier": "8.3.0", - "eslint-plugin-import": "2.22.1", + "eslint-plugin-import": "2.23.4", "eslint-plugin-jest": "24.3.6", "eslint-plugin-promise": "5.1.0", "glob": "7.1.7", "graphql": "15.5.0", "husky": "6.0.0", - "jest": "26.6.3", - "jest-circus": "26.6.3", + "jest": "27.0.4", + "jest-circus": "27.0.4", "jest-extended": "0.11.5", - "jest-junit": "12.0.0", + "jest-junit": "12.2.0", "jest-mock-extended": "1.0.15", "jest-silent-reporter": "0.5.0", "markdownlint-cli2": "0.1.3", "mockdate": "3.0.5", - "nock": "13.0.11", + "nock": "13.1.0", "npm-run-all": "4.1.5", - "prettier": "2.3.0", + "prettier": "2.3.1", "pretty-quick": "3.1.0", "rimraf": "3.0.2", "semantic-release": "17.4.3", "shelljs": "0.8.4", "strip-ansi": "6.0.0", "tmp-promise": "3.0.2", - "ts-jest": "26.5.6", + "ts-jest": "27.0.3", "ts-node": "10.0.0", - "type-fest": "1.1.3", - "typescript": "4.2.4", + "type-fest": "1.2.0", + "typescript": "4.3.2", "unified": "9.2.1" }, "resolutions": { - "underscore": "^1.12.1", - "jest-silent-reporter/jest-util": ">=25.1.0", - "jest-junit/jest-validate": ">=25.1.0", - "trim": "^1.0.0", - "**/acorn": ">=6.4.1 <7.0.0 || >=7.1.1", - "**/kind-of": ">=6.0.3", - "**/lock-verify/@iarna/cli": ">=2" + "**/css-what": "^5.0.1", + "**/kind-of": ">=6.0.3" }, "files": [ "dist" diff --git a/test/http-mock.ts b/test/http-mock.ts index 85c87181609cbd..931e42036a1501 100644 --- a/test/http-mock.ts +++ b/test/http-mock.ts @@ -1,9 +1,12 @@ import { Url } from 'url'; +import { afterAll, afterEach, beforeAll } from '@jest/globals'; import is from '@sindresorhus/is'; import { parse as parseGraphqlQuery } from 'graphql/language'; +// eslint-disable-next-line no-restricted-imports import nock from 'nock'; -export type { Scope } from 'nock'; +// eslint-disable-next-line no-restricted-imports +export type { Scope, ReplyHeaders } from 'nock'; interface RequestLogItem { headers: Record; @@ -95,28 +98,24 @@ function onMissing(req: TestRequest, opts?: TestRequest): void { } } -export function setup(): void { - if (!nock.isActive()) { - nock.activate(); - } - nock.disableNetConnect(); - nock.emitter.on('no match', onMissing); +export function allUsed(): boolean { + return nock.isDone(); } -export function reset(): void { - nock.emitter.removeListener('no match', onMissing); +/** + * Clear nock state. Will be called in `afterEach` + * @argument throwOnPending Use `false` to simply clear mocks. + */ +export function clear(throwOnPending = true): void { + const isDone = nock.isDone(); + const pending = nock.pendingMocks(); nock.abortPendingRequests(); - if (nock.isActive()) { - nock.restore(); - } nock.cleanAll(); requestLog = []; missingLog = []; - nock.enableNetConnect(); -} - -export function allUsed(): boolean { - return nock.isDone(); + if (!isDone && throwOnPending) { + throw new Error(`Pending mocks!\n * ${pending.join('\n * ')}`); + } } export function scope(basePath: BasePath, options?: nock.Options): nock.Scope { @@ -162,3 +161,20 @@ export function getTrace(): RequestLogItem[] /* istanbul ignore next */ { } return requestLog; } + +// init nock +beforeAll(() => { + nock.emitter.on('no match', onMissing); + nock.disableNetConnect(); +}); + +// clean nock to clear memory leack from http module patching +afterAll(() => { + nock.emitter.removeListener('no match', onMissing); + nock.restore(); +}); + +// clear nock state +afterEach(() => { + clear(); +}); diff --git a/tools/generate-imports.mjs b/tools/generate-imports.mjs index db13c8cddd3187..4633a0d09e6440 100644 --- a/tools/generate-imports.mjs +++ b/tools/generate-imports.mjs @@ -28,7 +28,10 @@ async function updateFile(file, code) { newFiles.add(file); } -const dataPaths = ['data']; +const dataPaths = [ + 'data', + 'node_modules/emojibase-data/en/shortcodes/github.json', +]; /** * diff --git a/tools/package.json b/tools/package.json index 2483811f52cead..924d63bf789583 100644 --- a/tools/package.json +++ b/tools/package.json @@ -2,11 +2,11 @@ "private": true, "type": "module", "dependencies": { - "@actions/core": "1.2.7", - "@jest/reporters": "26.6.2", - "@jest/test-result": "26.6.2", + "@actions/core": "1.4.0", + "@jest/reporters": "27.0.4", + "@jest/test-result": "27.0.2", "commander": "7.2.0", - "eslint": "7.26.0", + "eslint": "7.28.0", "fs-extra": "10.0.0", "got": "11.8.2", "lodash": "4.17.21", diff --git a/tsconfig.json b/tsconfig.json index 0099ae0294bbe2..f3cee72242a32a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,7 @@ "esModuleInterop": true, "resolveJsonModule": false, "noUnusedLocals": true, + "experimentalDecorators": true, "lib": ["es2018"], "types": ["node", "jest", "jest-extended"], "allowJs": true, diff --git a/yarn.lock b/yarn.lock index dacec44da9d865..fd2971c08e7e12 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@actions/core@1.2.7": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.2.7.tgz#594f8c45b213f0146e4be7eda8ae5cf4e198e5ab" - integrity sha512-kzLFD5BgEvq6ubcxdgPbRKGD2Qrgya/5j+wh4LZzqT915I0V3rED+MvjH6NXghbvk1MXknpNNQ3uKjXSEN00Ig== +"@actions/core@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.4.0.tgz#cf2e6ee317e314b03886adfeb20e448d50d6e524" + integrity sha512-CGx2ilGq5i7zSLgiiGUtBCxhRRxibJYU6Fim0Q1Wg2aQL2LTnF27zbqZOrxfvFQ55eSBW0L8uVStgtKMpa0Qlg== "@arcanis/slice-ansi@^1.0.2": version "1.0.2" @@ -356,9 +356,9 @@ integrity sha512-HqDPRdMzseVD4I/8Bb8TBAzg2X0U7oDiPfvYcvZt8fpVO2SwBOiLMh9tiEnRin48uRBbQMAw8D8wmCpyU78Dvg== "@aws-sdk/types@^3.1.0": - version "3.13.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.13.1.tgz#f69efe2053b0fa5dfd4046cb80dc176ad645c016" - integrity sha512-4eHboRz3I8f0C85Ta1dJ1v1Y9T1zH9xpC4/DufSIfQcD1Imc2U2LM22Qgbz8/PoP4kyhp2nJpQpW0APD91ILfw== + version "3.18.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.18.0.tgz#2158f054b83ea1319c47306bf08245fb26edeed0" + integrity sha512-fyk6HXK1wk83n4fDvsG+ewV+yS4uegepeMNrmLr7iBKjzc/bLckTWk7GKFM5ZaF/9jWyk7o2eKW3C3BltgDrfQ== "@aws-sdk/url-parser-native@3.4.1": version "3.4.1" @@ -424,9 +424,9 @@ tslib "^1.8.0" "@aws-sdk/util-locate-window@^3.0.0": - version "3.13.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.13.1.tgz#45c6df057be89c3f42ffd2e43c87a7837a3fa1ac" - integrity sha512-u1neaf5yO5FdnYF+UHsyDpHzHgMfX87nVDMyOyVvViIIhwDb2+bzzhUbex1rPtTEUfZUtgABV03UZrifGrB15g== + version "3.18.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.18.0.tgz#47bb20b6f9fcff45ec948e125a4e8f892f029d80" + integrity sha512-Lj2O9KaXCn+gPW23l3ydcSWe4HK0jH6teeSymbaFTwTjKtr4oLfDDKAOFoG5YyppQstEPqsL/RidVey4kOFfcg== dependencies: tslib "^2.0.0" @@ -463,9 +463,9 @@ tslib "^1.8.0" "@aws-sdk/util-utf8-browser@^3.0.0": - version "3.13.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.13.1.tgz#eae1f3d8063566d813c0df4adf5f487bd128b56f" - integrity sha512-+1FmtFOvDOYfoJnC6DEgjpcPKUERZA8VZ7JenY6SsEqVneWzHf4YVE2+KZM0DT9leLzgZBW/DKJWjeKxykaBEg== + version "3.18.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.18.0.tgz#d7d68290a323e4f9eb4f1d3f6add618c17e01a36" + integrity sha512-JwcdTb6AAMtnlt2Sg0I18DBK1sWlsfDR/23CkDQ52niXvCSRdHeNkh5b7SdEPVUKI76hyce9nEshzI1OasTv7w== dependencies: tslib "^2.0.0" @@ -500,25 +500,25 @@ dependencies: "@babel/highlight" "^7.12.13" -"@babel/compat-data@^7.13.15": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.0.tgz#a901128bce2ad02565df95e6ecbf195cf9465919" - integrity sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q== +"@babel/compat-data@^7.14.4": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.4.tgz#45720fe0cecf3fd42019e1d12cc3d27fadc98d58" + integrity sha512-i2wXrWQNkH6JplJQGn3Rd2I4Pij8GdHkXwHMxm+zV5YG/Jci+bCNrWZEWC4o+umiDkRrRs4dVzH3X4GP7vyjQQ== -"@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.0.tgz#47299ff3ec8d111b493f1a9d04bf88c04e728d88" - integrity sha512-8YqpRig5NmIHlMLw09zMlPTvUVMILjqCOtVgu+TVNWEBvy9b5I3RRyhqnrV4hjgEK7n8P9OqvkWJAFmEL6Wwfw== +"@babel/core@^7.1.0", "@babel/core@^7.7.2", "@babel/core@^7.7.5": + version "7.14.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.3.tgz#5395e30405f0776067fbd9cf0884f15bfb770a38" + integrity sha512-jB5AmTKOCSJIZ72sd78ECEhuPiDMKlQdDI/4QRI6lzYATx5SSogS1oQA2AoPecRCknm30gHi2l+QVvNUu3wZAg== dependencies: "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.14.0" + "@babel/generator" "^7.14.3" "@babel/helper-compilation-targets" "^7.13.16" - "@babel/helper-module-transforms" "^7.14.0" + "@babel/helper-module-transforms" "^7.14.2" "@babel/helpers" "^7.14.0" - "@babel/parser" "^7.14.0" + "@babel/parser" "^7.14.3" "@babel/template" "^7.12.13" - "@babel/traverse" "^7.14.0" - "@babel/types" "^7.14.0" + "@babel/traverse" "^7.14.2" + "@babel/types" "^7.14.2" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -526,33 +526,33 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.14.0": - version "7.14.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.1.tgz#1f99331babd65700183628da186f36f63d615c93" - integrity sha512-TMGhsXMXCP/O1WtQmZjpEYDhCYC9vFhayWZPJSZCGkPJgUqX0rF0wwtrYvnzVxIjcF80tkUertXVk5cwqi5cAQ== +"@babel/generator@^7.14.2", "@babel/generator@^7.14.3", "@babel/generator@^7.7.2": + version "7.14.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.3.tgz#0c2652d91f7bddab7cccc6ba8157e4f40dcedb91" + integrity sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA== dependencies: - "@babel/types" "^7.14.1" + "@babel/types" "^7.14.2" jsesc "^2.5.1" source-map "^0.5.0" "@babel/helper-compilation-targets@^7.13.16": - version "7.13.16" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz#6e91dccf15e3f43e5556dffe32d860109887563c" - integrity sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA== + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.4.tgz#33ebd0ffc34248051ee2089350a929ab02f2a516" + integrity sha512-JgdzOYZ/qGaKTVkn5qEDV/SXAh8KcyUVkCoSWGN8T3bwrgd6m+/dJa2kVGi6RJYJgEYPBdZ84BZp9dUjNWkBaA== dependencies: - "@babel/compat-data" "^7.13.15" + "@babel/compat-data" "^7.14.4" "@babel/helper-validator-option" "^7.12.17" - browserslist "^4.14.5" + browserslist "^4.16.6" semver "^6.3.0" -"@babel/helper-function-name@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz#93ad656db3c3c2232559fd7b2c3dbdcbe0eb377a" - integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA== +"@babel/helper-function-name@^7.14.2": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz#397688b590760b6ef7725b5f0860c82427ebaac2" + integrity sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ== dependencies: "@babel/helper-get-function-arity" "^7.12.13" "@babel/template" "^7.12.13" - "@babel/types" "^7.12.13" + "@babel/types" "^7.14.2" "@babel/helper-get-function-arity@^7.12.13": version "7.12.13" @@ -575,10 +575,10 @@ dependencies: "@babel/types" "^7.13.12" -"@babel/helper-module-transforms@^7.14.0": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.0.tgz#8fcf78be220156f22633ee204ea81f73f826a8ad" - integrity sha512-L40t9bxIuGOfpIGA3HNkJhU9qYrf4y5A5LUSw7rGMSn+pcG8dfJ0g6Zval6YJGd2nEjI7oP00fRdnhLKndx6bw== +"@babel/helper-module-transforms@^7.14.2": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz#ac1cc30ee47b945e3e0c4db12fa0c5389509dfe5" + integrity sha512-OznJUda/soKXv0XhpvzGWDnml4Qnwp16GN+D/kZIdLsWoHj05kyu8Rm5kXmMef+rVJZ0+4pSGLkeixdqNUATDA== dependencies: "@babel/helper-module-imports" "^7.13.12" "@babel/helper-replace-supers" "^7.13.12" @@ -586,8 +586,8 @@ "@babel/helper-split-export-declaration" "^7.12.13" "@babel/helper-validator-identifier" "^7.14.0" "@babel/template" "^7.12.13" - "@babel/traverse" "^7.14.0" - "@babel/types" "^7.14.0" + "@babel/traverse" "^7.14.2" + "@babel/types" "^7.14.2" "@babel/helper-optimise-call-expression@^7.12.13": version "7.12.13" @@ -602,14 +602,14 @@ integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== "@babel/helper-replace-supers@^7.13.12": - version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz#6442f4c1ad912502481a564a7386de0c77ff3804" - integrity sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw== + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.4.tgz#b2ab16875deecfff3ddfcd539bc315f72998d836" + integrity sha512-zZ7uHCWlxfEAAOVDYQpEf/uyi1dmeC7fX4nCf2iz9drnCwi1zvwXL3HwWWNXUQEJ1k23yVn3VbddiI9iJEXaTQ== dependencies: "@babel/helper-member-expression-to-functions" "^7.13.12" "@babel/helper-optimise-call-expression" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.12" + "@babel/traverse" "^7.14.2" + "@babel/types" "^7.14.4" "@babel/helper-simple-access@^7.13.12": version "7.13.12" @@ -653,10 +653,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.14.0": - version "7.14.1" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.1.tgz#1bd644b5db3f5797c4479d89ec1817fe02b84c47" - integrity sha512-muUGEKu8E/ftMTPlNp+mc6zL3E9zKWmF5sDHZ5MSsoTP9Wyz64AhEf9kD08xYJ7w6Hdcu8H550ircnPyWSIF0Q== +"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.14.2", "@babel/parser@^7.14.3", "@babel/parser@^7.7.2": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.4.tgz#a5c560d6db6cd8e6ed342368dea8039232cbab18" + integrity sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -742,6 +742,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.13.tgz#9dff111ca64154cef0f4dc52cf843d9f12ce4474" + integrity sha512-cHP3u1JiUiG2LFDKbXnwVad81GvfyIOmCD6HIEId6ojrY0Drfy2q1jw7BwN7dE84+kTnBjLkXoL3IEy/3JPu2w== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/runtime-corejs3@^7.12.1": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.14.0.tgz#6bf5fbc0b961f8e3202888cb2cd0fb7a0a9a3f66" @@ -759,24 +766,24 @@ "@babel/parser" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.0": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.0.tgz#cea0dc8ae7e2b1dec65f512f39f3483e8cc95aef" - integrity sha512-dZ/a371EE5XNhTHomvtuLTUyx6UEoJmYX+DT5zBCQN3McHemsuIaKKYqsc/fs26BEkHs/lBZy0J571LP5z9kQA== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.14.2", "@babel/traverse@^7.7.2": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.2.tgz#9201a8d912723a831c2679c7ebbf2fe1416d765b" + integrity sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA== dependencies: "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.14.0" - "@babel/helper-function-name" "^7.12.13" + "@babel/generator" "^7.14.2" + "@babel/helper-function-name" "^7.14.2" "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/parser" "^7.14.0" - "@babel/types" "^7.14.0" + "@babel/parser" "^7.14.2" + "@babel/types" "^7.14.2" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.13.12", "@babel/types@^7.14.0", "@babel/types@^7.14.1", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.14.1" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.1.tgz#095bd12f1c08ab63eff6e8f7745fa7c9cc15a9db" - integrity sha512-S13Qe85fzLs3gYRUnrpyeIrBJIMYv33qSTg1qoBwiG6nPKwUWAD9odSzWhEedpwOIzSEI6gbdQIWEMiCI42iBA== +"@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.13.12", "@babel/types@^7.14.0", "@babel/types@^7.14.2", "@babel/types@^7.14.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.4.tgz#bfd6980108168593b38b3eb48a24aa026b919bc0" + integrity sha512-lCj4aIs0xUefJFQnwwQv2Bxg7Omd6bgquZ6LGC+gGMh6/s5qDVfjuCMlDmYQ15SLsWHd9n+X3E75lKIhl5Lkiw== dependencies: "@babel/helper-validator-identifier" "^7.14.0" to-fast-properties "^2.0.0" @@ -791,37 +798,21 @@ resolved "https://registry.yarnpkg.com/@breejs/later/-/later-4.0.2.tgz#38c85cc98b717c7a196f87238090adaea01f8c9e" integrity sha512-EN0SlbyYouBdtZis1htdsgGlwFePzkXPwdIeqaBaavxkJT1G2/bitc2LSixjv45z2njXslxlJI1mW2O/Gmrb+A== -"@cnakazawa/watch@^1.0.3": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" - integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== - dependencies: - exec-sh "^0.3.2" - minimist "^1.2.0" - -"@eslint/eslintrc@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.1.tgz#442763b88cecbe3ee0ec7ca6d6dd6168550cbf14" - integrity sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ== +"@eslint/eslintrc@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179" + integrity sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg== dependencies: ajv "^6.12.4" debug "^4.1.1" espree "^7.3.0" - globals "^12.1.0" + globals "^13.9.0" ignore "^4.0.6" import-fresh "^3.2.1" js-yaml "^3.13.1" minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@iarna/cli@>=2": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@iarna/cli/-/cli-2.1.0.tgz#f830356d54c72c804bd7afc43999de31e40fc3d6" - integrity sha512-rvVVqDa2g860niRbqs3D5RhL4la3dc1vwk+NlpKPZxKaMSHtE2se6C2x8NeveN+rcjp3/686X+u+09CZ+7lmAQ== - dependencies: - glob "^7.1.2" - signal-exit "^3.0.2" - "@iarna/toml@2.2.5": version "2.2.5" resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" @@ -852,93 +843,94 @@ chalk "^2.0.1" slash "^2.0.0" -"@jest/console@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" - integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== +"@jest/console@^27.0.2": + version "27.0.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.0.2.tgz#b8eeff8f21ac51d224c851e1729d2630c18631e6" + integrity sha512-/zYigssuHLImGeMAACkjI4VLAiiJznHgAl3xnFT19iWyct2LhrH3KXOjHRmxBGTkiPLZKKAJAgaPpiU9EZ9K+w== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.0.2" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^26.6.2" - jest-util "^26.6.2" + jest-message-util "^27.0.2" + jest-util "^27.0.2" slash "^3.0.0" -"@jest/core@^26.6.3": - version "26.6.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" - integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== +"@jest/core@^27.0.4": + version "27.0.4" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.0.4.tgz#679bf9ac07900da2ddbb9667bb1afa8029038f53" + integrity sha512-+dsmV8VUs1h/Szb+rEWk8xBM1fp1I///uFy9nk3wXGvRsF2lBp8EVPmtWc+QFRb3MY2b7u2HbkGF1fzoDzQTLA== dependencies: - "@jest/console" "^26.6.2" - "@jest/reporters" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.0.2" + "@jest/reporters" "^27.0.4" + "@jest/test-result" "^27.0.2" + "@jest/transform" "^27.0.2" + "@jest/types" "^27.0.2" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" + emittery "^0.8.1" exit "^0.1.2" graceful-fs "^4.2.4" - jest-changed-files "^26.6.2" - jest-config "^26.6.3" - jest-haste-map "^26.6.2" - jest-message-util "^26.6.2" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-resolve-dependencies "^26.6.3" - jest-runner "^26.6.3" - jest-runtime "^26.6.3" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" - jest-watcher "^26.6.2" - micromatch "^4.0.2" + jest-changed-files "^27.0.2" + jest-config "^27.0.4" + jest-haste-map "^27.0.2" + jest-message-util "^27.0.2" + jest-regex-util "^27.0.1" + jest-resolve "^27.0.4" + jest-resolve-dependencies "^27.0.4" + jest-runner "^27.0.4" + jest-runtime "^27.0.4" + jest-snapshot "^27.0.4" + jest-util "^27.0.2" + jest-validate "^27.0.2" + jest-watcher "^27.0.2" + micromatch "^4.0.4" p-each-series "^2.1.0" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" - integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== +"@jest/environment@^27.0.3": + version "27.0.3" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.0.3.tgz#68769b1dfdd213e3456169d64fbe9bd63a5fda92" + integrity sha512-pN9m7fbKsop5vc3FOfH8NF7CKKdRbEZzcxfIo1n2TT6ucKWLFq0P6gCJH0GpnQp036++yY9utHOxpeT1WnkWTA== dependencies: - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/fake-timers" "^27.0.3" + "@jest/types" "^27.0.2" "@types/node" "*" - jest-mock "^26.6.2" + jest-mock "^27.0.3" -"@jest/fake-timers@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" - integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== +"@jest/fake-timers@^27.0.3": + version "27.0.3" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.0.3.tgz#9899ba6304cc636734c74478df502e18136461dd" + integrity sha512-fQ+UCKRIYKvTCEOyKPnaPnomLATIhMnHC/xPZ7yT1Uldp7yMgMxoYIFidDbpSTgB79+/U+FgfoD30c6wg3IUjA== dependencies: - "@jest/types" "^26.6.2" - "@sinonjs/fake-timers" "^6.0.1" + "@jest/types" "^27.0.2" + "@sinonjs/fake-timers" "^7.0.2" "@types/node" "*" - jest-message-util "^26.6.2" - jest-mock "^26.6.2" - jest-util "^26.6.2" + jest-message-util "^27.0.2" + jest-mock "^27.0.3" + jest-util "^27.0.2" -"@jest/globals@26.6.2", "@jest/globals@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" - integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== +"@jest/globals@27.0.3", "@jest/globals@^27.0.3": + version "27.0.3" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.0.3.tgz#1cf8933b7791bba0b99305cbf39fd4d2e3fe4060" + integrity sha512-OzsIuf7uf+QalqAGbjClyezzEcLQkdZ+7PejUrZgDs+okdAK8GwRCGcYCirHvhMBBQh60Jr3NlIGbn/KBPQLEQ== dependencies: - "@jest/environment" "^26.6.2" - "@jest/types" "^26.6.2" - expect "^26.6.2" + "@jest/environment" "^27.0.3" + "@jest/types" "^27.0.2" + expect "^27.0.2" -"@jest/reporters@26.6.2", "@jest/reporters@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" - integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== +"@jest/reporters@27.0.4", "@jest/reporters@^27.0.4": + version "27.0.4" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.0.4.tgz#95609b1be97afb80d55d8aa3d7c3179c15810e65" + integrity sha512-Xa90Nm3JnV0xCe4M6A10M9WuN9krb+WFKxV1A98Y4ePCw40n++r7uxFUNU7DT1i9Behj7fjrAIju9oU0t1QtCg== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.0.2" + "@jest/test-result" "^27.0.2" + "@jest/transform" "^27.0.2" + "@jest/types" "^27.0.2" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" @@ -949,17 +941,15 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.0.2" - jest-haste-map "^26.6.2" - jest-resolve "^26.6.2" - jest-util "^26.6.2" - jest-worker "^26.6.2" + jest-haste-map "^27.0.2" + jest-resolve "^27.0.4" + jest-util "^27.0.2" + jest-worker "^27.0.2" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" terminal-link "^2.0.0" v8-to-istanbul "^7.0.0" - optionalDependencies: - node-notifier "^8.0.0" "@jest/source-map@^24.9.0": version "24.9.0" @@ -970,22 +960,22 @@ graceful-fs "^4.1.15" source-map "^0.6.0" -"@jest/source-map@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" - integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== +"@jest/source-map@^27.0.1": + version "27.0.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.0.1.tgz#2afbf73ddbaddcb920a8e62d0238a0a9e0a8d3e4" + integrity sha512-yMgkF0f+6WJtDMdDYNavmqvbHtiSpwRN2U/W+6uztgfqgkq/PXdKPqjBTUF1RD/feth4rH5N3NW0T5+wIuln1A== dependencies: callsites "^3.0.0" graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/test-result@26.6.2", "@jest/test-result@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" - integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== +"@jest/test-result@27.0.2", "@jest/test-result@^27.0.2": + version "27.0.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.0.2.tgz#0451049e32ceb609b636004ccc27c8fa22263f10" + integrity sha512-gcdWwL3yP5VaIadzwQtbZyZMgpmes8ryBAJp70tuxghiA8qL4imJyZex+i+USQH2H4jeLVVszhwntgdQ97fccA== dependencies: - "@jest/console" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.0.2" + "@jest/types" "^27.0.2" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" @@ -998,33 +988,32 @@ "@jest/types" "^24.9.0" "@types/istanbul-lib-coverage" "^2.0.0" -"@jest/test-sequencer@^26.6.3": - version "26.6.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" - integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== +"@jest/test-sequencer@^27.0.4": + version "27.0.4" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.0.4.tgz#976493b277594d81e589896f0ed21f198308928a" + integrity sha512-6UFEVwdmxYdyNffBxVVZxmXEdBE4riSddXYSnFNH0ELFQFk/bvagizim8WfgJTqF4EKd+j1yFxvhb8BMHfOjSQ== dependencies: - "@jest/test-result" "^26.6.2" + "@jest/test-result" "^27.0.2" graceful-fs "^4.2.4" - jest-haste-map "^26.6.2" - jest-runner "^26.6.3" - jest-runtime "^26.6.3" + jest-haste-map "^27.0.2" + jest-runtime "^27.0.4" -"@jest/transform@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" - integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== +"@jest/transform@^27.0.2": + version "27.0.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.0.2.tgz#b073b7c589e3f4b842102468875def2bb722d6b5" + integrity sha512-H8sqKlgtDfVog/s9I4GG2XMbi4Ar7RBxjsKQDUhn2XHAi3NG+GoQwWMER+YfantzExbjNqQvqBHzo/G2pfTiPw== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^26.6.2" + "@jest/types" "^27.0.2" babel-plugin-istanbul "^6.0.0" chalk "^4.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" graceful-fs "^4.2.4" - jest-haste-map "^26.6.2" - jest-regex-util "^26.0.0" - jest-util "^26.6.2" - micromatch "^4.0.2" + jest-haste-map "^27.0.2" + jest-regex-util "^27.0.1" + jest-util "^27.0.2" + micromatch "^4.0.4" pirates "^4.0.1" slash "^3.0.0" source-map "^0.6.1" @@ -1050,6 +1039,17 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@jest/types@^27.0.2": + version "27.0.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.0.2.tgz#e153d6c46bda0f2589f0702b071f9898c7bbd37e" + integrity sha512-XpjCtJ/99HB4PmyJ2vgmN7vT+JLP7RW1FBT9RgnMFS4Dt7cvIyBee8O3/j98aUZ34ZpenPZFqmaaObWSeL65dg== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + "@kwsites/file-exists@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz#ad1efcac13e1987d8dbaf235ef3be5b0d96faa99" @@ -1067,31 +1067,31 @@ resolved "https://registry.yarnpkg.com/@ls-lint/ls-lint/-/ls-lint-1.9.2.tgz#689f1f4c06072823a726802ba167340efcefe19c" integrity sha512-sugEjWjSSy9OHF6t1ZBLZCAROj52cZthB9dIePmzZzzMwmWwy3qAEMSdJheHeS1FOwDZI7Ipm1H/bWgzJNnSAw== -"@nodelib/fs.scandir@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" - integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: - "@nodelib/fs.stat" "2.0.4" + "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" - integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" - integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== + version "1.2.7" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz#94c23db18ee4653e129abd26fb06f870ac9e1ee2" + integrity sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA== dependencies: - "@nodelib/fs.scandir" "2.1.4" + "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@npmcli/arborist@^2.0.0", "@npmcli/arborist@^2.3.0", "@npmcli/arborist@^2.4.2": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-2.4.2.tgz#7c22eb0d7b66f31e250e1927047d0bd497dbdf8a" - integrity sha512-QrsMrRWzO1D2EmPQheyPz1yRnnmln6vPe4SujV4cRF0v9qIAQbD8M0dMH6K3y+w/2X3t7vg5lx20LHXsbcu7lw== +"@npmcli/arborist@^2.3.0", "@npmcli/arborist@^2.5.0", "@npmcli/arborist@^2.6.1": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-2.6.2.tgz#74ec5741afa6b6bf62603443793e33fc7a4f1245" + integrity sha512-CAo0HSziRdlpGUUheERmOrADnKHfBYpLAl/HmWGwGCtWKB3BCxfgb0rJ7MsFg38wy7YF3+fDs7R9dMVCH89K/A== dependencies: "@npmcli/installed-package-contents" "^1.0.7" "@npmcli/map-workspaces" "^1.0.2" @@ -1104,12 +1104,12 @@ cacache "^15.0.3" common-ancestor-path "^1.0.1" json-parse-even-better-errors "^2.3.1" - json-stringify-nice "^1.1.2" + json-stringify-nice "^1.1.4" mkdirp-infer-owner "^2.0.0" npm-install-checks "^4.0.0" npm-package-arg "^8.1.0" npm-pick-manifest "^6.1.0" - npm-registry-fetch "^10.0.0" + npm-registry-fetch "^11.0.0" pacote "^11.2.6" parse-conflict-json "^1.1.1" promise-all-reject-late "^1.0.0" @@ -1251,18 +1251,18 @@ universal-user-agent "^6.0.0" "@octokit/graphql@^4.5.8": - version "4.6.1" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.1.tgz#f975486a46c94b7dbe58a0ca751935edc7e32cc9" - integrity sha512-2lYlvf4YTDgZCTXTW4+OX+9WTLFtEUc6hGm4qM1nlZjzxj+arizM4aHWzBVBCxY9glh7GIs0WEuiSgbVzv8cmA== + version "4.6.2" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.2.tgz#ec44abdfa87f2b9233282136ae33e4ba446a04e7" + integrity sha512-WmsIR1OzOr/3IqfG9JIczI8gMJUMzzyx5j0XXQ4YihHtKlQc+u35VpVoOXhlKAlaBntvry1WpAzPl/a+s3n89Q== dependencies: "@octokit/request" "^5.3.0" "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-7.0.0.tgz#0f6992db9854af15eca77d71ab0ec7fad2f20411" - integrity sha512-gV/8DJhAL/04zjTI95a7FhQwS6jlEE0W/7xeYAzuArD0KVAVWDLP2f3vi98hs3HLTczxXdRK/mF0tRoQPpolEw== +"@octokit/openapi-types@^7.2.3": + version "7.3.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-7.3.0.tgz#1d9ed79828513c57a95e6360b7c9b4749503e79d" + integrity sha512-o00X2FCLiEeXZkm1Ab5nvPUdVOlrpediwWZkpizUJ/xtZQsJ4FiQ2RB/dJEmb0Nk+NIz7zyDePcSCu/Y/0M3Ew== "@octokit/plugin-paginate-rest@^2.6.2": version "2.13.3" @@ -1276,12 +1276,12 @@ resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d" integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ== -"@octokit/plugin-rest-endpoint-methods@5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.1.tgz#631b8d4edc6798b03489911252a25f2a4e58c594" - integrity sha512-vvWbPtPqLyIzJ7A4IPdTl+8IeuKAwMJ4LjvmqWOOdfSuqWQYZXq2CEd0hsnkidff2YfKlguzujHs/reBdAx8Sg== +"@octokit/plugin-rest-endpoint-methods@5.3.1": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.3.1.tgz#deddce769b4ec3179170709ab42e4e9e6195aaa9" + integrity sha512-3B2iguGmkh6bQQaVOtCsS0gixrz8Lg0v4JuXPqBcFqLKuJtxAUf3K88RxMEf/naDOI73spD+goJ/o7Ie7Cvdjg== dependencies: - "@octokit/types" "^6.13.1" + "@octokit/types" "^6.16.2" deprecation "^2.3.1" "@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.5": @@ -1294,33 +1294,33 @@ once "^1.4.0" "@octokit/request@^5.3.0", "@octokit/request@^5.4.12": - version "5.4.15" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.15.tgz#829da413dc7dd3aa5e2cdbb1c7d0ebe1f146a128" - integrity sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag== + version "5.5.0" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.5.0.tgz#6588c532255b8e71886cefa0d2b64b4ad73bf18c" + integrity sha512-jxbMLQdQ3heFMZUaTLSCqcKs2oAHEYh7SnLLXyxbZmlULExZ/RXai7QUWWFKowcGGPlCZuKTZg0gSKHWrfYEoQ== dependencies: "@octokit/endpoint" "^6.0.1" "@octokit/request-error" "^2.0.0" - "@octokit/types" "^6.7.1" + "@octokit/types" "^6.16.1" is-plain-object "^5.0.0" node-fetch "^2.6.1" universal-user-agent "^6.0.0" "@octokit/rest@^18.0.0": - version "18.5.3" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.3.tgz#6a2e6006a87ebbc34079c419258dd29ec9ff659d" - integrity sha512-KPAsUCr1DOdLVbZJgGNuE/QVLWEaVBpFQwDAz/2Cnya6uW2wJ/P5RVGk0itx7yyN1aGa8uXm2pri4umEqG1JBA== + version "18.5.6" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.6.tgz#8c9a7c9329c7bbf478af20df78ddeab0d21f6d89" + integrity sha512-8HdG6ZjQdZytU6tCt8BQ2XLC7EJ5m4RrbyU/EARSkAM1/HP3ceOzMG/9atEfe17EDMer3IVdHWLedz2wDi73YQ== dependencies: "@octokit/core" "^3.2.3" "@octokit/plugin-paginate-rest" "^2.6.2" "@octokit/plugin-request-log" "^1.0.2" - "@octokit/plugin-rest-endpoint-methods" "5.0.1" + "@octokit/plugin-rest-endpoint-methods" "5.3.1" -"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.13.1", "@octokit/types@^6.7.1": - version "6.14.2" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.14.2.tgz#64c9457f38fb8522bdbba3c8cc814590a2d61bf5" - integrity sha512-wiQtW9ZSy4OvgQ09iQOdyXYNN60GqjCL/UdMsepDr1Gr0QzpW6irIKbH3REuAHXAhxkEk9/F2a3Gcs1P6kW5jA== +"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.16.1", "@octokit/types@^6.16.2": + version "6.16.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.16.2.tgz#62242e0565a3eb99ca2fd376283fe78b4ea057b4" + integrity sha512-wWPSynU4oLy3i4KGyk+J1BLwRKyoeW2TwRHgwbDz17WtVFzSK2GOErGliruIx8c+MaYtHSYTx36DSmLNoNbtgA== dependencies: - "@octokit/openapi-types" "^7.0.0" + "@octokit/openapi-types" "^7.2.3" "@renovate/pep440@1.0.0": version "1.0.0" @@ -1367,9 +1367,9 @@ parse-json "^5.0.0" "@semantic-release/github@^7.0.0": - version "7.2.2" - resolved "https://registry.yarnpkg.com/@semantic-release/github/-/github-7.2.2.tgz#aa51ba7098c812de4acd73145660164705f5c05d" - integrity sha512-nYKxp0sfoFrHn2uNbhkon8oAxIffWPfu5LEEVs8I5Eqv/0IHvU+Kr3u8l4BTD+NNHA9ATTdy2bhQqQrZJ7wTvQ== + version "7.2.3" + resolved "https://registry.yarnpkg.com/@semantic-release/github/-/github-7.2.3.tgz#20a83abd42dca43d97f03553de970eac72856c85" + integrity sha512-lWjIVDLal+EQBzy697ayUNN8MoBpp+jYIyW2luOdqn5XBH4d9bQGfTnjuLyzARZBHejqh932HVjiH/j4+R7VHw== dependencies: "@octokit/rest" "^18.0.0" "@semantic-release/error" "^2.2.0" @@ -1435,10 +1435,10 @@ dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" - integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== +"@sinonjs/fake-timers@^7.0.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" + integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg== dependencies: "@sinonjs/commons" "^1.7.0" @@ -1474,7 +1474,7 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1" integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": version "7.1.14" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" integrity sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g== @@ -1551,18 +1551,18 @@ resolved "https://registry.yarnpkg.com/@types/emscripten/-/emscripten-1.39.4.tgz#d61990c0cee72c4e475de737a140b51fe925a2c8" integrity sha512-k3LLVMFrdNA9UCvMDPWMbFrGPNb+GcPyw29ktJTo1RCN7RmxFG5XzPZcPKRlnLuLT/FRm8wp4ohvDwNY7GlROQ== -"@types/eslint@7.2.10": - version "7.2.10" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.10.tgz#4b7a9368d46c0f8cd5408c23288a59aa2394d917" - integrity sha512-kUEPnMKrqbtpCq/KTaGFFKAcz6Ethm2EjCoKIDaCmfRBWLbFuTcOJfTlorwbnboXBzahqWLgUp1BQeKHiJzPUQ== +"@types/eslint@7.2.13": + version "7.2.13" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.13.tgz#e0ca7219ba5ded402062ad6f926d491ebb29dd53" + integrity sha512-LKmQCWAlnVHvvXq4oasNUMTJJb2GwSyTY8+1C7OH5ILR8mPLaljv1jxL1bXW3xB3jFbQxTKxJAvI8PyjB09aBg== dependencies: "@types/estree" "*" "@types/json-schema" "*" "@types/estree@*": - version "0.0.47" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4" - integrity sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg== + version "0.0.48" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.48.tgz#18dc8091b285df90db2f25aa7d906cfc394b7f74" + integrity sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew== "@types/fs-extra@9.0.11": version "9.0.11" @@ -1637,9 +1637,9 @@ "@types/istanbul-lib-report" "*" "@types/istanbul-reports@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" - integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== dependencies: "@types/istanbul-lib-report" "*" @@ -1661,7 +1661,7 @@ resolved "https://registry.yarnpkg.com/@types/json-dup-key-validator/-/json-dup-key-validator-1.0.0.tgz#3a666ab980e5e957960e9341e13eabd4fd9a24f3" integrity sha512-D/pvJeKintUSW4G+aBRSvReECxCHbMcE9IuSU7cfWQAtYStE/vH8OLXFw38JJ37dSEnpcOkEV7q4WmdvtV0zHg== -"@types/json-schema@*", "@types/json-schema@^7.0.3": +"@types/json-schema@*", "@types/json-schema@^7.0.7": version "7.0.7" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== @@ -1688,15 +1688,15 @@ resolved "https://registry.yarnpkg.com/@types/linkify-markdown/-/linkify-markdown-1.0.0.tgz#50c357a0a60bb220209f19310a79b939e1b8e2a3" integrity sha512-p3rl2HtugwjkH8qjEjUi5JByoZkVc2lz6E7D96X5/FqNyj2/jlgIrMlyrbck0U+HyjhOIy0XyTwES3RRz+1koQ== -"@types/luxon@1.26.5": - version "1.26.5" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.26.5.tgz#843fb705e16e4d2a90847a351b799ea9d879859e" - integrity sha512-XeQxxRMyJi1znfzHw4CGDLyup/raj84SnjjkI2fDootZPGlB0yqtvlvEIAmzHDa5wiEI5JJevZOWxpcofsaV+A== +"@types/luxon@1.27.0": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.27.0.tgz#1e3b5a7f8ca6944349c43498b4442b742c71ab0b" + integrity sha512-rr2lNXsErnA/ARtgFn46NtQjUa66cuwZYeo/2K7oqqxhJErhXgHBPyNKCo+pfOC3L7HFwtao8ebViiU9h4iAxA== -"@types/markdown-it@12.0.1": - version "12.0.1" - resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.0.1.tgz#8391e19fea4796ff863edda55800c7e669beb358" - integrity sha512-mHfT8j/XkPb1uLEfs0/C3se6nd+webC2kcqcy8tgcVr0GDEONv/xaQzAN+aQvkxQXk/jC0Q6mPS+0xhFwRF35g== +"@types/markdown-it@12.0.2": + version "12.0.2" + resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.0.2.tgz#153e5477970ed2a47b2f619ed4ab66f870de8a04" + integrity sha512-p4DIfLMmGN0iLSbMxknDXeSm8W2ZRqQeN/1EAwVxVqJietzgp3WeP1UQjCKWDXWBcEbUa1ECx8YAfdpQdDQmZQ== dependencies: "@types/highlight.js" "^9.7.0" "@types/linkify-it" "*" @@ -1747,19 +1747,19 @@ integrity sha512-0fRfA90FWm6KJfw6P9QGyo0HDTCmthZ7cWaBQndITlaWLTZ6njRyKwrwpzpg+n6kBXBIGKeUHEQuBx7bphGJkA== "@types/node@*": - version "15.0.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.2.tgz#51e9c0920d1b45936ea04341aa3e2e58d339fb67" - integrity sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA== + version "15.12.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.2.tgz#1f2b42c4be7156ff4a6f914b2fb03d05fa84e38d" + integrity sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww== -"@types/node@14.17.0": - version "14.17.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.0.tgz#3ba770047723b3eeb8dc9fca02cce8a7fb6378da" - integrity sha512-w8VZUN/f7SSbvVReb9SWp6cJFevxb4/nkG65yLAya//98WgocKm5PLDAtSs5CtJJJM+kHmJjO/6mmYW4MHShZA== +"@types/node@14.17.3": + version "14.17.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.3.tgz#6d327abaa4be34a74e421ed6409a0ae2f47f4c3d" + integrity sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw== "@types/node@^13.7.0": - version "13.13.51" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.51.tgz#a424c5282f99fc1ca41f11b727b6aea80668bcba" - integrity sha512-66/xg5I5Te4oGi5Jws11PtNmKkZbOPZWyBZZ/l5AOrWj1Dyw+6Ge/JhYTq/2/Yvdqyhrue8RL+DGI298OJ0xcg== + version "13.13.52" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.52.tgz#03c13be70b9031baaed79481c0c0cfb0045e53f7" + integrity sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -1776,15 +1776,15 @@ resolved "https://registry.yarnpkg.com/@types/parse-link-header/-/parse-link-header-1.0.0.tgz#69f059e40a0fa93dc2e095d4142395ae6adc5d7a" integrity sha512-fCA3btjE7QFeRLfcD0Sjg+6/CnmC66HpMBoRfRzd2raTaWMJV21CCZ0LO8MOqf8onl5n0EPfjq4zDhbyX8SVwA== -"@types/prettier@^2.0.0": +"@types/prettier@^2.1.5": version "2.2.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.3.tgz#ef65165aea2924c9359205bf748865b8881753c0" integrity sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA== "@types/redis@^2.8.27": - version "2.8.28" - resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.28.tgz#5862b2b64aa7f7cbc76dafd7e6f06992b52c55e3" - integrity sha512-8l2gr2OQ969ypa7hFOeKqtFoY70XkHxISV0pAwmQ2nm6CSPb1brmTmqJCGGrekCo+pAZyWlNXr+Kvo6L/1wijA== + version "2.8.29" + resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.29.tgz#f272dba985156e599e2523afb4f5f7e7586b56b5" + integrity sha512-/pjQ9lwnL/t1bEfRHQFEJB3kHCR65tpB19NEWmbqcgGgqrfeGo/9b4tUtHbClxQoy3+g6Esx2QRtV7fk7kBPYg== dependencies: "@types/node" "*" @@ -1815,16 +1815,11 @@ resolved "https://registry.yarnpkg.com/@types/semver-utils/-/semver-utils-1.1.0.tgz#d35645238680d2297a8c9b67395d8b37f0500895" integrity sha512-p3ZoozEL036SPVLpMh1LuDebYQkes1rhBfXSFCbact9/FF6dkvR1UV/UqNO5wN5tvdlq+4jK+xTwgWwqRlFUJA== -"@types/semver@7.3.6": +"@types/semver@7.3.6", "@types/semver@^7.1.0": version "7.3.6" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.6.tgz#e9831776f4512a7ba6da53e71c26e5fb67882d63" integrity sha512-0caWDWmpCp0uifxFh+FaqK3CuZ2SkRR/ZRxAV5+zNdC3QVUi6wyOJnefhPvtNt8NQWXB5OA93BUvZsXpWat2Xw== -"@types/semver@^7.1.0": - version "7.3.5" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.5.tgz#74deebbbcb1e86634dbf10a5b5e8798626f5a597" - integrity sha512-iotVxtCCsPLRAvxMFFgxL8HD2l4mAZ2Oin7/VJ2ooWO0VOK4EGOGmZWZn1uCq7RofR3I/1IOSjCHlFT71eVK0Q== - "@types/shelljs@0.8.8": version "0.8.8" resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.8.tgz#e439c69929b88a2c8123c1a55e09eb708315addf" @@ -1887,130 +1882,88 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz#29d3c9c81f6200b1fd6d8454cfb007ba176cde80" - integrity sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw== +"@types/yargs@^16.0.0": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.3.tgz#4b6d35bb8e680510a7dc2308518a80ee1ef27e01" + integrity sha512-YlFfTGS+zqCgXuXNV26rOIeETOkXnGQXP/pjjL9P0gO/EP9jTmc7pUBhx+jVEIxpq41RX33GQ7N3DzOSfZoglQ== dependencies: - "@typescript-eslint/experimental-utils" "4.23.0" - "@typescript-eslint/scope-manager" "4.23.0" - debug "^4.1.1" - functional-red-black-tree "^1.0.1" - lodash "^4.17.15" - regexpp "^3.0.0" - semver "^7.3.2" - tsutils "^3.17.1" - -"@typescript-eslint/experimental-utils@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz#f2059434cd6e5672bfeab2fb03b7c0a20622266f" - integrity sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.23.0" - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/typescript-estree" "4.23.0" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/experimental-utils@^4.0.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.1.tgz#3938a5c89b27dc9a39b5de63a62ab1623ab27497" - integrity sha512-svYlHecSMCQGDO2qN1v477ax/IDQwWhc7PRBiwAdAMJE7GXk5stF4Z9R/8wbRkuX/5e9dHqbIWxjeOjckK3wLQ== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.22.1" - "@typescript-eslint/types" "4.22.1" - "@typescript-eslint/typescript-estree" "4.22.1" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/parser@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.23.0.tgz#239315d38e42e852bef43a4b0b01bef78f78911c" - integrity sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug== - dependencies: - "@typescript-eslint/scope-manager" "4.23.0" - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/typescript-estree" "4.23.0" - debug "^4.1.1" + "@types/yargs-parser" "*" -"@typescript-eslint/parser@^4.4.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.22.1.tgz#a95bda0fd01d994a15fc3e99dc984294f25c19cc" - integrity sha512-l+sUJFInWhuMxA6rtirzjooh8cM/AATAe3amvIkqKFeMzkn85V+eLzb1RyuXkHak4dLfYzOmF6DXPyflJvjQnw== +"@types/yauzl@^2.9.1": + version "2.9.1" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" + integrity sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA== dependencies: - "@typescript-eslint/scope-manager" "4.22.1" - "@typescript-eslint/types" "4.22.1" - "@typescript-eslint/typescript-estree" "4.22.1" - debug "^4.1.1" + "@types/node" "*" -"@typescript-eslint/scope-manager@4.22.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.22.1.tgz#5bb357f94f9cd8b94e6be43dd637eb73b8f355b4" - integrity sha512-d5bAiPBiessSmNi8Amq/RuLslvcumxLmyhf1/Xa9IuaoFJ0YtshlJKxhlbY7l2JdEk3wS0EnmnfeJWSvADOe0g== +"@typescript-eslint/eslint-plugin@4.26.1": + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.1.tgz#b9c7313321cb837e2bf8bebe7acc2220659e67d3" + integrity sha512-aoIusj/8CR+xDWmZxARivZjbMBQTT9dImUtdZ8tVCVRXgBUuuZyM5Of5A9D9arQPxbi/0rlJLcuArclz/rCMJw== dependencies: - "@typescript-eslint/types" "4.22.1" - "@typescript-eslint/visitor-keys" "4.22.1" + "@typescript-eslint/experimental-utils" "4.26.1" + "@typescript-eslint/scope-manager" "4.26.1" + debug "^4.3.1" + functional-red-black-tree "^1.0.1" + lodash "^4.17.21" + regexpp "^3.1.0" + semver "^7.3.5" + tsutils "^3.21.0" -"@typescript-eslint/scope-manager@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz#8792ef7eacac122e2ec8fa2d30a59b8d9a1f1ce4" - integrity sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w== +"@typescript-eslint/experimental-utils@4.26.1", "@typescript-eslint/experimental-utils@^4.0.1": + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.1.tgz#a35980a2390da9232aa206b27f620eab66e94142" + integrity sha512-sQHBugRhrXzRCs9PaGg6rowie4i8s/iD/DpTB+EXte8OMDfdCG5TvO73XlO9Wc/zi0uyN4qOmX9hIjQEyhnbmQ== dependencies: - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/visitor-keys" "4.23.0" - -"@typescript-eslint/types@4.22.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.1.tgz#bf99c6cec0b4a23d53a61894816927f2adad856a" - integrity sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw== - -"@typescript-eslint/types@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.23.0.tgz#da1654c8a5332f4d1645b2d9a1c64193cae3aa3b" - integrity sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw== + "@types/json-schema" "^7.0.7" + "@typescript-eslint/scope-manager" "4.26.1" + "@typescript-eslint/types" "4.26.1" + "@typescript-eslint/typescript-estree" "4.26.1" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" -"@typescript-eslint/typescript-estree@4.22.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.1.tgz#dca379eead8cdfd4edc04805e83af6d148c164f9" - integrity sha512-p3We0pAPacT+onSGM+sPR+M9CblVqdA9F1JEdIqRVlxK5Qth4ochXQgIyb9daBomyQKAXbygxp1aXQRV0GC79A== +"@typescript-eslint/parser@4.26.1", "@typescript-eslint/parser@^4.4.1": + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.26.1.tgz#cecfdd5eb7a5c13aabce1c1cfd7fbafb5a0f1e8e" + integrity sha512-q7F3zSo/nU6YJpPJvQveVlIIzx9/wu75lr6oDbDzoeIRWxpoc/HQ43G4rmMoCc5my/3uSj2VEpg/D83LYZF5HQ== dependencies: - "@typescript-eslint/types" "4.22.1" - "@typescript-eslint/visitor-keys" "4.22.1" - debug "^4.1.1" - globby "^11.0.1" - is-glob "^4.0.1" - semver "^7.3.2" - tsutils "^3.17.1" + "@typescript-eslint/scope-manager" "4.26.1" + "@typescript-eslint/types" "4.26.1" + "@typescript-eslint/typescript-estree" "4.26.1" + debug "^4.3.1" -"@typescript-eslint/typescript-estree@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz#0753b292097523852428a6f5a1aa8ccc1aae6cd9" - integrity sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw== +"@typescript-eslint/scope-manager@4.26.1": + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.26.1.tgz#075a74a15ff33ee3a7ed33e5fce16ee86689f662" + integrity sha512-TW1X2p62FQ8Rlne+WEShyd7ac2LA6o27S9i131W4NwDSfyeVlQWhw8ylldNNS8JG6oJB9Ha9Xyc+IUcqipvheQ== dependencies: - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/visitor-keys" "4.23.0" - debug "^4.1.1" - globby "^11.0.1" - is-glob "^4.0.1" - semver "^7.3.2" - tsutils "^3.17.1" + "@typescript-eslint/types" "4.26.1" + "@typescript-eslint/visitor-keys" "4.26.1" -"@typescript-eslint/visitor-keys@4.22.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.1.tgz#6045ae25a11662c671f90b3a403d682dfca0b7a6" - integrity sha512-WPkOrIRm+WCLZxXQHCi+WG8T2MMTUFR70rWjdWYddLT7cEfb2P4a3O/J2U1FBVsSFTocXLCoXWY6MZGejeStvQ== +"@typescript-eslint/types@4.26.1": + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.26.1.tgz#9e7c523f73c34b04a765e4167ca5650436ef1d38" + integrity sha512-STyMPxR3cS+LaNvS8yK15rb8Y0iL0tFXq0uyl6gY45glyI7w0CsyqyEXl/Fa0JlQy+pVANeK3sbwPneCbWE7yg== + +"@typescript-eslint/typescript-estree@4.26.1": + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.1.tgz#b2ce2e789233d62283fae2c16baabd4f1dbc9633" + integrity sha512-l3ZXob+h0NQzz80lBGaykdScYaiEbFqznEs99uwzm8fPHhDjwaBFfQkjUC/slw6Sm7npFL8qrGEAMxcfBsBJUg== dependencies: - "@typescript-eslint/types" "4.22.1" - eslint-visitor-keys "^2.0.0" + "@typescript-eslint/types" "4.26.1" + "@typescript-eslint/visitor-keys" "4.26.1" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz#7215cc977bd3b4ef22467b9023594e32f9e4e455" - integrity sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg== +"@typescript-eslint/visitor-keys@4.26.1": + version "4.26.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.1.tgz#0d55ea735cb0d8903b198017d6d4f518fdaac546" + integrity sha512-IGouNSSd+6x/fHtYRyLOM6/C+QxMDzWlDtN41ea+flWuSF9g02iqcIlX8wM53JkfljoIjP0U+yp7SiTS1onEkw== dependencies: - "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/types" "4.26.1" eslint-visitor-keys "^2.0.0" "@yarnpkg/core@2.4.0": @@ -2141,10 +2094,15 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -"acorn@>=6.4.1 <7.0.0 || >=7.1.1", acorn@^7.1.1, acorn@^7.4.0, acorn@^8.1.0: - version "8.2.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.2.4.tgz#caba24b08185c3b56e3168e97d15ed17f4d31fd0" - integrity sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg== +acorn@^7.1.1, acorn@^7.4.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.2.4: + version "8.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.3.0.tgz#1193f9b96c4e8232f00b11a9edff81b2c8b98b88" + integrity sha512-tqPKHZ5CaBJw0Xmy0ZZvLs1qTV+BNFSyvn77ASXkpBNfIRk8ev26fKrD9iLGwGA9zedPao52GSHzq8lyZG0NUw== agent-base@6: version "6.0.2" @@ -2181,9 +2139,9 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.2.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.2.0.tgz#c89d3380a784ce81b2085f48811c4c101df4c602" - integrity sha512-WSNGFuyWd//XO8n/m/EaOlNLtO0yL8EXT/74LqT4khdhpZjP7lkj/kT5uwRmGitKEVp/Oj7ZUHeGfPtgHhQ5CA== + version "8.6.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.0.tgz#60cc45d9c46a477d80d92c48076d972c342e5720" + integrity sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2236,6 +2194,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.3.0: dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + ansicolors@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" @@ -2251,14 +2214,6 @@ any-promise@^1.1.0, any-promise@~1.3.0: resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - anymatch@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -2337,7 +2292,7 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= -array-includes@^3.1.1: +array-includes@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a" integrity sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A== @@ -2358,7 +2313,7 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -array.prototype.flat@^1.2.3: +array.prototype.flat@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123" integrity sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg== @@ -2432,16 +2387,16 @@ azure-devops-node-api@10.2.2: tunnel "0.0.6" typed-rest-client "^1.8.4" -babel-jest@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" - integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== +babel-jest@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.0.2.tgz#7dc18adb01322acce62c2af76ea2c7cd186ade37" + integrity sha512-9OThPl3/IQbo4Yul2vMz4FYwILPQak8XelX4YGowygfHaOl5R5gfjm4iVx4d8aUugkW683t8aq0A74E7b5DU1Q== dependencies: - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/babel__core" "^7.1.7" + "@jest/transform" "^27.0.2" + "@jest/types" "^27.0.2" + "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^26.6.2" + babel-preset-jest "^27.0.1" chalk "^4.0.0" graceful-fs "^4.2.4" slash "^3.0.0" @@ -2457,10 +2412,10 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^4.0.0" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" - integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== +babel-plugin-jest-hoist@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.0.1.tgz#a6d10e484c93abff0f4e95f437dad26e5736ea11" + integrity sha512-sqBF0owAcCDBVEDtxqfYr2F36eSHdx7lAVGyYuOBRnKdD6gzcy0I0XrAYCZgOA3CRrLhmR+Uae9nogPzmAtOfQ== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -2485,12 +2440,12 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" - integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== +babel-preset-jest@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.0.1.tgz#7a50c75d16647c23a2cf5158d5bb9eb206b10e20" + integrity sha512-nIBIqCEpuiyhvjQs2mVNwTxQQa2xk70p9Dd/0obQGBf8FBzbnI8QhQKzLsWMN2i6q+5B0OcWDtrboBX5gmOLyA== dependencies: - babel-plugin-jest-hoist "^26.6.2" + babel-plugin-jest-hoist "^27.0.1" babel-preset-current-node-syntax "^1.0.0" backslash@^0.2.0: @@ -2534,9 +2489,9 @@ bcrypt-pbkdf@^1.0.0: tweetnacl "^0.14.3" before-after-hook@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.1.tgz#73540563558687586b52ed217dad6a802ab1549c" - integrity sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw== + version "2.2.2" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" + integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== bin-links@^2.2.1: version "2.2.1" @@ -2575,9 +2530,9 @@ boolbase@^1.0.0: integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= boolean@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.3.tgz#0fee0c9813b66bef25a8a6a904bb46736d05f024" - integrity sha512-EqrTKXQX6Z3A2nRmMEIlAIfjQOgFnVO2nqZGpbcsPnYGWBwpFqzlrozU1dy+S2iqfYDLh26ef4KrgTxu9xQrxA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.1.0.tgz#a245080649ebb80e7c0ea218a480e4e913136336" + integrity sha512-K6r5tvO1ykeYerI7jIyTvSFw2l6D6DzqkljGj2E2uyYAAdDo2SV4qGJIV75cHIQpTFyb6BB0BEHiDdDrFsNI+g== bottleneck@^2.18.1: version "2.19.5" @@ -2625,7 +2580,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.14.5: +browserslist@^4.16.6: version "4.16.6" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== @@ -2650,6 +2605,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + buffer-from@1.x, buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -2683,33 +2643,10 @@ byte-size@^7.0.1: resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== -cacache@15.1.0: - version "15.1.0" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.1.0.tgz#164c2f857ee606e4cc793c63018fefd0ea5eba7b" - integrity sha512-mfx0C+mCfWjD1PnwQ9yaOrwG1ou9FkKnx0SvzUHWdFt7r7GaRtzT+9M8HAvLu62zIHtnpQ/1m93nWNDCckJGXQ== - dependencies: - "@npmcli/move-file" "^1.0.1" - chownr "^2.0.0" - fs-minipass "^2.0.0" - glob "^7.1.4" - infer-owner "^1.0.4" - lru-cache "^6.0.0" - minipass "^3.1.1" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^1.0.3" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.0.2" - unique-filename "^1.1.1" - -cacache@^15.0.3, cacache@^15.0.5, cacache@^15.0.6: - version "15.0.6" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.6.tgz#65a8c580fda15b59150fb76bf3f3a8e45d583099" - integrity sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w== +cacache@15.2.0, cacache@^15.0.3, cacache@^15.0.5, cacache@^15.2.0: + version "15.2.0" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.2.0.tgz#73af75f77c58e72d8c630a7a2858cb18ef523389" + integrity sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw== dependencies: "@npmcli/move-file" "^1.0.1" chownr "^2.0.0" @@ -2789,22 +2726,15 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0: +camelcase@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== caniuse-lite@^1.0.30001219: - version "1.0.30001223" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001223.tgz#39b49ff0bfb3ee3587000d2f66c47addc6e14443" - integrity sha512-k/RYs6zc/fjbxTjaWZemeSmOjO0JJV+KguOBA3NwPup8uzxM1cMhR2BD9XmO86GuqaqTCO8CgkgH9Rz//vdDiA== - -capture-exit@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" - integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== - dependencies: - rsvp "^4.8.4" + version "1.0.30001235" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz#ad5ca75bc5a1f7b12df79ad806d715a43a5ac4ed" + integrity sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A== cardinal@^2.1.1: version "2.1.1" @@ -2879,6 +2809,11 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +ci-info@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" + integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== + cidr-regex@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/cidr-regex/-/cidr-regex-3.1.1.tgz#ba1972c57c66f61875f18fd7dd487469770b571d" @@ -2886,10 +2821,10 @@ cidr-regex@^3.1.1: dependencies: ip-regex "^4.1.0" -cjs-module-lexer@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" - integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== +cjs-module-lexer@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.1.tgz#2fd46d9906a126965aa541345c499aaa18e8cd73" + integrity sha512-jVamGdJPDeuQilKhvVn1h3knuMOZzr8QDnpk+M9aMlCaMkTDd6fBWPhiDqFvFZ07pL0liqabAiuy8SY4jGHeaw== class-utils@^0.3.5: version "0.3.6" @@ -2941,15 +2876,6 @@ clipanion@^2.6.2: resolved "https://registry.yarnpkg.com/clipanion/-/clipanion-2.6.2.tgz#820e7440812052442455b248f927b187ed732f71" integrity sha512-0tOHJNMF9+4R3qcbBL+4IxLErpaYSYvzs10aXuECDbZdJOuJHdagJMAqvLdeaUQTI/o2uSCDRpet6ywDiKOAYw== -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -3048,7 +2974,7 @@ columnify@~1.5.4: strip-ansi "^3.0.0" wcwidth "^1.0.0" -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -3093,11 +3019,6 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= -contains-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" - integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= - conventional-changelog-angular@^5.0.0: version "5.0.12" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz#c979b8b921cbfe26402eb3da5bbfda02d865a2b9" @@ -3175,14 +3096,14 @@ copy-descriptor@^0.1.0: integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-js-pure@^3.0.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.12.0.tgz#c59d45954a6569232f0704d085916a5e8c3b272f" - integrity sha512-j2y084taJU4VMUpwuC93l19tsPbTAtOpg6/do3UOwX4eUJbsFdhEaGRQfTYthn5rDubsB88YITtei0Kw46vEQQ== + version "3.14.0" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.14.0.tgz#72bcfacba74a65ffce04bf94ae91d966e80ee553" + integrity sha512-YVh+LN2FgNU0odThzm61BsdkwrbrchumFq3oztnE9vTKC4KS2fvnPmcx8t6jnqAyOTCTF4ZSiuK8Qhh7SNcL4g== core-js@^3.6.5: - version "3.12.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.12.0.tgz#62bac86f7d7f087d40dba3e90a211c2c3c8559ea" - integrity sha512-SaMnchL//WwU2Ot1hhkPflE8gzo7uq1FGvUJ8GKmi3TOU7rGTHIU+eir1WGf6qOtTyxdfdcp10yPdGZ59sQ3hw== + version "3.14.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.14.0.tgz#62322b98c71cc2018b027971a69419e2425c2a6c" + integrity sha512-3s+ed8er9ahK+zJpp9ZtuVcDoFzHNiZsPbNAAE4KXgrRHbjSqqNN6xGSXq6bq7TZIbKj4NLrLb6bJ5i+vSVjHA== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -3221,7 +3142,7 @@ cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, c shebang-command "^2.0.0" which "^2.0.1" -cross-spawn@^6.0.0, cross-spawn@^6.0.5: +cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -3244,21 +3165,21 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== -css-select@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-3.1.2.tgz#d52cbdc6fee379fba97fb0d3925abbd18af2d9d8" - integrity sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA== +css-select@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067" + integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA== dependencies: boolbase "^1.0.0" - css-what "^4.0.0" - domhandler "^4.0.0" - domutils "^2.4.3" + css-what "^5.0.0" + domhandler "^4.2.0" + domutils "^2.6.0" nth-check "^2.0.0" -css-what@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-4.0.0.tgz#35e73761cab2eeb3d3661126b23d7aa0e8432233" - integrity sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A== +css-what@^5.0.0, css-what@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad" + integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg== cssom@^0.4.4: version "0.4.4" @@ -3317,6 +3238,13 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: dependencies: ms "2.0.0" +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -3462,10 +3390,10 @@ dequal@2.0.2: resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d" integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug== -detect-indent@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" - integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA== +detect-indent@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== detect-newline@^3.0.0: version "3.1.0" @@ -3473,9 +3401,9 @@ detect-newline@^3.0.0: integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== detect-node@^2.0.4: - version "2.0.5" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.5.tgz#9d270aa7eaa5af0b72c4c9d9b814e7f4ce738b79" - integrity sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw== + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== dezalgo@^1.0.0: version "1.0.3" @@ -3495,6 +3423,11 @@ diff-sequences@^26.6.2: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== +diff-sequences@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.1.tgz#9c9801d52ed5f576ff0a20e3022a13ee6e297e7c" + integrity sha512-XPLijkfJUh/PIBnfkcSHgvD6tlYixmcMAn3osTk6jt+H0v/mgURto1XUiD9DKuGX5NDoVS6dSlA23gd9FUaCFg== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -3512,13 +3445,12 @@ dir-glob@^3.0.0, dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -doctrine@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" - integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" - isarray "^1.0.0" doctrine@^3.0.0: version "3.0.0" @@ -3528,12 +3460,12 @@ doctrine@^3.0.0: esutils "^2.0.2" dom-serializer@^1.0.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.1.tgz#d845a1565d7c041a95e5dab62184ab41e3a519be" - integrity sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q== + version "1.3.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== dependencies: domelementtype "^2.0.1" - domhandler "^4.0.0" + domhandler "^4.2.0" entities "^2.0.0" domelementtype@^2.0.1, domelementtype@^2.2.0: @@ -3548,17 +3480,17 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" -domhandler@^4.0.0, domhandler@^4.2.0: +domhandler@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA== dependencies: domelementtype "^2.2.0" -domutils@^2.4.3: - version "2.6.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.6.0.tgz#2e15c04185d43fb16ae7057cb76433c6edb938b7" - integrity sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA== +domutils@^2.6.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442" + integrity sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg== dependencies: dom-serializer "^1.0.1" domelementtype "^2.2.0" @@ -3594,25 +3526,45 @@ ecc-jsbn@~0.1.1: safer-buffer "^2.1.0" electron-to-chromium@^1.3.723: - version "1.3.727" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz#857e310ca00f0b75da4e1db6ff0e073cc4a91ddf" - integrity sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg== + version "1.3.749" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.749.tgz#0ecebc529ceb49dd2a7c838ae425236644c3439a" + integrity sha512-F+v2zxZgw/fMwPz/VUGIggG4ZndDsYy0vlpthi3tjmDZlcfbhN5mYW0evXUsBr2sUtuDANFtle410A9u/sd/4A== email-addresses@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/email-addresses/-/email-addresses-4.0.0.tgz#94fa214c30f943b02eaf91da717d89ff6a19e345" integrity sha512-Nas3sSSiD5lSIoqBos0FMjB9h4clHxXuAahHKGJ5doRWavEB7pBHzOxnI7R5f1MuGNrrSnsZFJ81HCBv0DZmnw== -emittery@^0.7.1: - version "0.7.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" - integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== +emittery@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== + +emoji-regex@9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emojibase-data@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/emojibase-data/-/emojibase-data-6.2.0.tgz#db6c75c36905284fa623f4aa5f468d2be6ed364a" + integrity sha512-SWKaXD2QeQs06IE7qfJftsI5924Dqzp+V9xaa5RzZIEWhmlrG6Jt2iKwfgOPHu+5S8MEtOI7GdpKsXj46chXOw== + +emojibase-regex@5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/emojibase-regex/-/emojibase-regex-5.1.3.tgz#f0ef621ed6ec624becd2326f999fd4ea01b94554" + integrity sha512-gT8T9LxLA8VJdI+8KQtyykB9qKzd7WuUL3M2yw6y9tplFeufOUANg3UKVaKUvkMcRNvZsSElWhxcJrx8WPE12g== + +emojibase@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/emojibase/-/emojibase-5.2.0.tgz#3b53899f9a9f8aae1280db446c83ce28f1d286d5" + integrity sha512-5T02oTJaWpScAtYbukKVc8vQ1367MyfVtFHUMoOVZ9/r1kFcbYqjSktD56TICBAeyW9uc1t+7qQuXEtntM6p5A== + encoding@^0.1.12: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -3669,17 +3621,17 @@ err-code@^2.0.2: resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== -error-ex@^1.2.0, error-ex@^1.3.1: +error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" -es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: - version "1.18.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" - integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== +es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.18.2: + version "1.18.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" + integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" @@ -3689,14 +3641,14 @@ es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: has-symbols "^1.0.2" is-callable "^1.2.3" is-negative-zero "^2.0.1" - is-regex "^1.1.2" - is-string "^1.0.5" - object-inspect "^1.9.0" + is-regex "^1.1.3" + is-string "^1.0.6" + object-inspect "^1.10.3" object-keys "^1.1.1" object.assign "^4.1.2" string.prototype.trimend "^1.0.4" string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.0" + unbox-primitive "^1.0.1" es-to-primitive@^1.2.1: version "1.2.1" @@ -3784,31 +3736,33 @@ eslint-import-resolver-node@^0.3.4: debug "^2.6.9" resolve "^1.13.1" -eslint-module-utils@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" - integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== +eslint-module-utils@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz#b51be1e473dd0de1c5ea638e22429c2490ea8233" + integrity sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A== dependencies: - debug "^2.6.9" + debug "^3.2.7" pkg-dir "^2.0.0" -eslint-plugin-import@2.22.1: - version "2.22.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702" - integrity sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw== +eslint-plugin-import@2.23.4: + version "2.23.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz#8dceb1ed6b73e46e50ec9a5bb2411b645e7d3d97" + integrity sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ== dependencies: - array-includes "^3.1.1" - array.prototype.flat "^1.2.3" - contains-path "^0.1.0" + array-includes "^3.1.3" + array.prototype.flat "^1.2.4" debug "^2.6.9" - doctrine "1.5.0" + doctrine "^2.1.0" eslint-import-resolver-node "^0.3.4" - eslint-module-utils "^2.6.0" + eslint-module-utils "^2.6.1" + find-up "^2.0.0" has "^1.0.3" + is-core-module "^2.4.0" minimatch "^3.0.4" - object.values "^1.1.1" - read-pkg-up "^2.0.0" - resolve "^1.17.0" + object.values "^1.1.3" + pkg-up "^2.0.0" + read-pkg-up "^3.0.0" + resolve "^1.20.0" tsconfig-paths "^3.9.0" eslint-plugin-jest@24.3.6: @@ -3823,7 +3777,7 @@ eslint-plugin-promise@5.1.0: resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz#fb2188fb734e4557993733b41aa1a688f46c6f24" integrity sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng== -eslint-scope@^5.0.0, eslint-scope@^5.1.1: +eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -3831,13 +3785,20 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.0.0, eslint-utils@^2.1.0: +eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" @@ -3848,28 +3809,30 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@7.26.0: - version "7.26.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.26.0.tgz#d416fdcdcb3236cd8f282065312813f8c13982f6" - integrity sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg== +eslint@7.28.0: + version "7.28.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.28.0.tgz#435aa17a0b82c13bb2be9d51408b617e49c1e820" + integrity sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g== dependencies: "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.1" + "@eslint/eslintrc" "^0.4.2" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.0.1" doctrine "^3.0.0" enquirer "^2.3.5" + escape-string-regexp "^4.0.0" eslint-scope "^5.1.1" eslint-utils "^2.1.0" eslint-visitor-keys "^2.0.0" espree "^7.3.1" esquery "^1.4.0" esutils "^2.0.2" + fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" - glob-parent "^5.0.0" + glob-parent "^5.1.2" globals "^13.6.0" ignore "^4.0.6" import-fresh "^3.0.0" @@ -3878,7 +3841,7 @@ eslint@7.26.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.21" + lodash.merge "^4.6.2" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -3887,7 +3850,7 @@ eslint@7.26.0: semver "^7.2.1" strip-ansi "^6.0.0" strip-json-comments "^3.1.0" - table "^6.0.4" + table "^6.0.9" text-table "^0.2.0" v8-compile-cache "^2.0.3" @@ -3939,24 +3902,6 @@ eventemitter3@^4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -exec-sh@^0.3.2: - version "0.3.6" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" - integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" @@ -3973,9 +3918,9 @@ execa@^4.0.0: strip-final-newline "^2.0.0" execa@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" - integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" get-stream "^6.0.0" @@ -4017,17 +3962,17 @@ expect@^24.1.0: jest-message-util "^24.9.0" jest-regex-util "^24.9.0" -expect@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" - integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== +expect@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.0.2.tgz#e66ca3a4c9592f1c019fa1d46459a9d2084f3422" + integrity sha512-YJFNJe2+P2DqH+ZrXy+ydRQYO87oxRUonZImpDodR1G7qo3NYd3pL+NQ9Keqpez3cehczYwZDBC3A7xk3n7M/w== dependencies: - "@jest/types" "^26.6.2" - ansi-styles "^4.0.0" - jest-get-type "^26.3.0" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-regex-util "^26.0.0" + "@jest/types" "^27.0.2" + ansi-styles "^5.0.0" + jest-get-type "^27.0.1" + jest-matcher-utils "^27.0.2" + jest-message-util "^27.0.2" + jest-regex-util "^27.0.1" extend-shallow@^2.0.1: version "2.0.1" @@ -4063,6 +4008,17 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +extract-zip@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -4078,7 +4034,7 @@ fast-base64-decode@^1.0.0: resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" integrity sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q== -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -4124,6 +4080,13 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -4220,6 +4183,15 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -4270,7 +4242,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.1.2: +fsevents@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -4304,7 +4276,7 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-caller-file@^2.0.1, get-caller-file@^2.0.5: +get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -4323,13 +4295,6 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-stream@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" @@ -4397,14 +4362,14 @@ github-url-from-git@1.5.0: resolved "https://registry.yarnpkg.com/github-url-from-git/-/github-url-from-git-1.5.0.tgz#f985fedcc0a9aa579dc88d7aff068d55cc6251a0" integrity sha1-+YX+3MCpqledyI16/waNVcxiUaA= -glob-parent@^5.0.0, glob-parent@^5.1.0: +glob-parent@^5.1.0, glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@7.1.7: +glob@7.1.7, glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== @@ -4427,18 +4392,6 @@ glob@^6.0.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - global-agent@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.2.0.tgz#566331b0646e6bf79429a16877685c4a1fbf76dc" @@ -4457,17 +4410,10 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^12.1.0: - version "12.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" - integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== - dependencies: - type-fest "^0.8.1" - -globals@^13.6.0: - version "13.8.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.8.0.tgz#3e20f504810ce87a8d72e55aecf8435b50f4c1b3" - integrity sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q== +globals@^13.6.0, globals@^13.9.0: + version "13.9.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.9.0.tgz#4bf2bf635b334a173fb1daf7c5e6b218ecdc06cb" + integrity sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA== dependencies: type-fest "^0.20.2" @@ -4478,7 +4424,7 @@ globalthis@^1.0.1: dependencies: define-properties "^1.1.3" -globby@^11.0.0, globby@^11.0.1, globby@~11.0.3: +globby@^11.0.0, globby@^11.0.1, globby@^11.0.3, globby@~11.0.3: version "11.0.3" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== @@ -4522,11 +4468,6 @@ graphql@15.5.0: resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5" integrity sha512-OmaM7y0kaK31NKG31q4YbD2beNYa6jBBKtMFT6gLYJljHLJr42IqJ8KX08u3Li/0ifzTU5HjmoOOrwa5BRLeDA== -growly@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" - integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= - handlebars@4.7.7, handlebars@^4.7.6: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" @@ -4738,9 +4679,9 @@ iconv-lite@0.4.24: safer-buffer ">= 2.1.2 < 3" iconv-lite@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" - integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ== + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" @@ -4750,9 +4691,9 @@ ieee754@^1.1.13: integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore-walk@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" - integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== + version "3.0.4" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" + integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== dependencies: minimatch "^3.0.4" @@ -4907,11 +4848,11 @@ is-bigint@^1.0.1: integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== is-boolean-object@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0" - integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" + integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== dependencies: - call-bind "^1.0.0" + call-bind "^1.0.2" is-buffer@^2.0.0: version "2.0.5" @@ -4930,6 +4871,13 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" +is-ci@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" + integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== + dependencies: + ci-info "^3.1.1" + is-cidr@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/is-cidr/-/is-cidr-4.0.2.tgz#94c7585e4c6c77ceabf920f8cde51b8c0fda8814" @@ -4937,10 +4885,10 @@ is-cidr@^4.0.2: dependencies: cidr-regex "^3.1.1" -is-core-module@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.3.0.tgz#d341652e3408bca69c4671b79a0954a3d349f887" - integrity sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw== +is-core-module@^2.2.0, is-core-module@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" + integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== dependencies: has "^1.0.3" @@ -4959,9 +4907,9 @@ is-data-descriptor@^1.0.0: kind-of "^6.0.0" is-date-object@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.3.tgz#4c0802ae9c8097939ea8001eaae3c502f3dbe72f" - integrity sha512-tDpEUInNcy2Yw3lNSepK3Wdw1RnXLcIVienz6Ou631Acl15cJyRWK4dgA1vCmOEgIbtOV0W7MHg+AR2Gdg1NXQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" + integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== is-decimal@^1.0.0: version "1.0.4" @@ -4986,11 +4934,6 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" -is-docker@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -5053,9 +4996,9 @@ is-negative-zero@^2.0.1: integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== is-number-object@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" - integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" + integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== is-number@^3.0.0: version "3.0.0" @@ -5106,47 +5049,42 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-potential-custom-element-name@^1.0.0: +is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== -is-regex@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" - integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== +is-regex@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" + integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== dependencies: call-bind "^1.0.2" - has-symbols "^1.0.1" + has-symbols "^1.0.2" is-ssh@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.2.tgz#a4b82ab63d73976fd8263cceee27f99a88bdae2b" - integrity sha512-elEw0/0c2UscLrNG+OAorbP539E3rhliKPg+hDMWN9VwrDXfYK+4PBEykDPfxlYYtQvl84TascnQyobfQLHEhQ== + version "1.3.3" + resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.3.tgz#7f133285ccd7f2c2c7fc897b771b53d95a2b2c7e" + integrity sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ== dependencies: protocols "^1.1.0" -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - is-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-string@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" - integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== +is-string@^1.0.5, is-string@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" + integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: - has-symbols "^1.0.1" + has-symbols "^1.0.2" is-text-path@^1.0.1: version "1.0.1" @@ -5165,19 +5103,12 @@ is-windows@^1.0.2: resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - is@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/is/-/is-3.3.0.tgz#61cff6dd3c4193db94a3d62582072b44e5645d79" integrity sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg== -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: +isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -5261,84 +5192,84 @@ java-properties@^1.0.0: resolved "https://registry.yarnpkg.com/java-properties/-/java-properties-1.0.2.tgz#ccd1fa73907438a5b5c38982269d0e771fe78211" integrity sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ== -jest-changed-files@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" - integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== +jest-changed-files@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.0.2.tgz#997253042b4a032950fc5f56abf3c5d1f8560801" + integrity sha512-eMeb1Pn7w7x3wue5/vF73LPCJ7DKQuC9wQUR5ebP9hDPpk5hzcT/3Hmz3Q5BOFpR3tgbmaWhJcMTVgC8Z1NuMw== dependencies: - "@jest/types" "^26.6.2" - execa "^4.0.0" - throat "^5.0.0" + "@jest/types" "^27.0.2" + execa "^5.0.0" + throat "^6.0.1" -jest-circus@26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-26.6.3.tgz#3cc7ef2a6a3787e5d7bfbe2c72d83262154053e7" - integrity sha512-ACrpWZGcQMpbv13XbzRzpytEJlilP/Su0JtNCi5r/xLpOUhnaIJr8leYYpLEMgPFURZISEHrnnpmB54Q/UziPw== +jest-circus@27.0.4, jest-circus@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.0.4.tgz#3b261514ee3b3da33def736a6352c98ff56bb6e6" + integrity sha512-QD+eblDiRphta630WRKewuASLs/oY1Zki2G4bccntRvrTHQ63ljwFR5TLduuK4Zg0ZPzW0+8o6AP7KRd1yKOjw== dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/babel__traverse" "^7.0.4" + "@jest/environment" "^27.0.3" + "@jest/test-result" "^27.0.2" + "@jest/types" "^27.0.2" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" - expect "^26.6.2" + expect "^27.0.2" is-generator-fn "^2.0.0" - jest-each "^26.6.2" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-runner "^26.6.3" - jest-runtime "^26.6.3" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - pretty-format "^26.6.2" - stack-utils "^2.0.2" - throat "^5.0.0" + jest-each "^27.0.2" + jest-matcher-utils "^27.0.2" + jest-message-util "^27.0.2" + jest-runtime "^27.0.4" + jest-snapshot "^27.0.4" + jest-util "^27.0.2" + pretty-format "^27.0.2" + slash "^3.0.0" + stack-utils "^2.0.3" + throat "^6.0.1" -jest-cli@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" - integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== +jest-cli@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.0.4.tgz#491b12c754c0d7c6873b13a66f26b3a80a852910" + integrity sha512-E0T+/i2lxsWAzV7LKYd0SB7HUAvePqaeIh5vX43/G5jXLhv1VzjYzJAGEkTfvxV774ll9cyE2ljcL73PVMEOXQ== dependencies: - "@jest/core" "^26.6.3" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/core" "^27.0.4" + "@jest/test-result" "^27.0.2" + "@jest/types" "^27.0.2" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.4" import-local "^3.0.2" - is-ci "^2.0.0" - jest-config "^26.6.3" - jest-util "^26.6.2" - jest-validate "^26.6.2" + jest-config "^27.0.4" + jest-util "^27.0.2" + jest-validate "^27.0.2" prompts "^2.0.1" - yargs "^15.4.1" + yargs "^16.0.3" -jest-config@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" - integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== +jest-config@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.0.4.tgz#c4f41378acf40ca77860fb4e213b12109d87b8cf" + integrity sha512-VkQFAHWnPQefdvHU9A+G3H/Z3NrrTKqWpvxgQz3nkUdkDTWeKJE6e//BL+R7z79dXOMVksYgM/z6ndtN0hfChg== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^26.6.3" - "@jest/types" "^26.6.2" - babel-jest "^26.6.3" + "@jest/test-sequencer" "^27.0.4" + "@jest/types" "^27.0.2" + babel-jest "^27.0.2" chalk "^4.0.0" deepmerge "^4.2.2" glob "^7.1.1" graceful-fs "^4.2.4" - jest-environment-jsdom "^26.6.2" - jest-environment-node "^26.6.2" - jest-get-type "^26.3.0" - jest-jasmine2 "^26.6.3" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" - micromatch "^4.0.2" - pretty-format "^26.6.2" + is-ci "^3.0.0" + jest-circus "^27.0.4" + jest-environment-jsdom "^27.0.3" + jest-environment-node "^27.0.3" + jest-get-type "^27.0.1" + jest-jasmine2 "^27.0.4" + jest-regex-util "^27.0.1" + jest-resolve "^27.0.4" + jest-runner "^27.0.4" + jest-util "^27.0.2" + jest-validate "^27.0.2" + micromatch "^4.0.4" + pretty-format "^27.0.2" jest-diff@^24.9.0: version "24.9.0" @@ -5350,7 +5281,7 @@ jest-diff@^24.9.0: jest-get-type "^24.9.0" pretty-format "^24.9.0" -jest-diff@^26.0.0, jest-diff@^26.6.2: +jest-diff@^26.0.0: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== @@ -5360,48 +5291,58 @@ jest-diff@^26.0.0, jest-diff@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" -jest-docblock@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" - integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== +jest-diff@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.0.2.tgz#f315b87cee5dc134cf42c2708ab27375cc3f5a7e" + integrity sha512-BFIdRb0LqfV1hBt8crQmw6gGQHVDhM87SpMIZ45FPYKReZYG5er1+5pIn2zKqvrJp6WNox0ylR8571Iwk2Dmgw== dependencies: - detect-newline "^3.0.0" - -jest-each@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" - integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== - dependencies: - "@jest/types" "^26.6.2" chalk "^4.0.0" - jest-get-type "^26.3.0" - jest-util "^26.6.2" - pretty-format "^26.6.2" + diff-sequences "^27.0.1" + jest-get-type "^27.0.1" + pretty-format "^27.0.2" -jest-environment-jsdom@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" - integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== +jest-docblock@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.0.1.tgz#bd9752819b49fa4fab1a50b73eb58c653b962e8b" + integrity sha512-TA4+21s3oebURc7VgFV4r7ltdIJ5rtBH1E3Tbovcg7AV+oLfD5DcJ2V2vJ5zFA9sL5CFd/d2D6IpsAeSheEdrA== dependencies: - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - jest-mock "^26.6.2" - jest-util "^26.6.2" - jsdom "^16.4.0" + detect-newline "^3.0.0" -jest-environment-node@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" - integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== +jest-each@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.0.2.tgz#865ddb4367476ced752167926b656fa0dcecd8c7" + integrity sha512-OLMBZBZ6JkoXgUenDtseFRWA43wVl2BwmZYIWQws7eS7pqsIvePqj/jJmEnfq91ALk3LNphgwNK/PRFBYi7ITQ== dependencies: - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/types" "^27.0.2" + chalk "^4.0.0" + jest-get-type "^27.0.1" + jest-util "^27.0.2" + pretty-format "^27.0.2" + +jest-environment-jsdom@^27.0.3: + version "27.0.3" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.0.3.tgz#ed73e913ddc03864eb9f934b5cbabf1b63504e2e" + integrity sha512-5KLmgv1bhiimpSA8oGTnZYk6g4fsNyZiA/6gI2tAZUgrufd7heRUSVh4gRokzZVEj8zlwAQYT0Zs6tuJSW/ECA== + dependencies: + "@jest/environment" "^27.0.3" + "@jest/fake-timers" "^27.0.3" + "@jest/types" "^27.0.2" + "@types/node" "*" + jest-mock "^27.0.3" + jest-util "^27.0.2" + jsdom "^16.6.0" + +jest-environment-node@^27.0.3: + version "27.0.3" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.0.3.tgz#b4acb3679d2552a4215732cab8b0ca7ec4398ee0" + integrity sha512-co2/IVnIFL3cItpFULCvXFg9us4gvWXgs7mutAMPCbFhcqh56QAOdKhNzC2+RycsC/k4mbMj1VF+9F/NzA0ROg== + dependencies: + "@jest/environment" "^27.0.3" + "@jest/fake-timers" "^27.0.3" + "@jest/types" "^27.0.2" "@types/node" "*" - jest-mock "^26.6.2" - jest-util "^26.6.2" + jest-mock "^27.0.3" + jest-util "^27.0.2" jest-extended@0.11.5: version "0.11.5" @@ -5427,68 +5368,72 @@ jest-get-type@^26.3.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-haste-map@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" - integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== +jest-get-type@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.1.tgz#34951e2b08c8801eb28559d7eb732b04bbcf7815" + integrity sha512-9Tggo9zZbu0sHKebiAijyt1NM77Z0uO4tuWOxUCujAiSeXv30Vb5D4xVF4UR4YWNapcftj+PbByU54lKD7/xMg== + +jest-haste-map@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.0.2.tgz#3f1819400c671237e48b4d4b76a80a0dbed7577f" + integrity sha512-37gYfrYjjhEfk37C4bCMWAC0oPBxDpG0qpl8lYg8BT//wf353YT/fzgA7+Dq0EtM7rPFS3JEcMsxdtDwNMi2cA== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.0.2" "@types/graceful-fs" "^4.1.2" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.4" - jest-regex-util "^26.0.0" - jest-serializer "^26.6.2" - jest-util "^26.6.2" - jest-worker "^26.6.2" - micromatch "^4.0.2" - sane "^4.0.3" + jest-regex-util "^27.0.1" + jest-serializer "^27.0.1" + jest-util "^27.0.2" + jest-worker "^27.0.2" + micromatch "^4.0.4" walker "^1.0.7" optionalDependencies: - fsevents "^2.1.2" + fsevents "^2.3.2" -jest-jasmine2@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" - integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== +jest-jasmine2@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.0.4.tgz#c669519ccf4904a485338555e1e66cad36bb0670" + integrity sha512-yj3WrjjquZwkJw+eA4c9yucHw4/+EHndHWSqgHbHGQfT94ihaaQsa009j1a0puU8CNxPDk0c1oAPeOpdJUElwA== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^26.6.2" - "@jest/source-map" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/environment" "^27.0.3" + "@jest/source-map" "^27.0.1" + "@jest/test-result" "^27.0.2" + "@jest/types" "^27.0.2" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^26.6.2" + expect "^27.0.2" is-generator-fn "^2.0.0" - jest-each "^26.6.2" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-runtime "^26.6.3" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - pretty-format "^26.6.2" - throat "^5.0.0" - -jest-junit@12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-12.0.0.tgz#3ebd4a6a84b50c4ab18323a8f7d9cceb9d845df6" - integrity sha512-+8K35LlboWiPuCnXSyiid7rFdxNlpCWWM20WEYe6IZH6psfUWKZmSpSRQ5tk0C0cBeDsvsnIzcef5mYhyJsbug== + jest-each "^27.0.2" + jest-matcher-utils "^27.0.2" + jest-message-util "^27.0.2" + jest-runtime "^27.0.4" + jest-snapshot "^27.0.4" + jest-util "^27.0.2" + pretty-format "^27.0.2" + throat "^6.0.1" + +jest-junit@12.2.0: + version "12.2.0" + resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-12.2.0.tgz#cff7f9516e84f8e30f6bdea04cd84db6b095a376" + integrity sha512-ecGzF3KEQwLbMP5xMO7wqmgmyZlY/5yWDvgE/vFa+/uIT0KsU5nluf0D2fjIlOKB+tb6DiuSSpZuGpsmwbf7Fw== dependencies: mkdirp "^1.0.4" strip-ansi "^5.2.0" - uuid "^3.3.3" + uuid "^8.3.2" xml "^1.0.1" -jest-leak-detector@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" - integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== +jest-leak-detector@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.0.2.tgz#ce19aa9dbcf7a72a9d58907a970427506f624e69" + integrity sha512-TZA3DmCOfe8YZFIMD1GxFqXUkQnIoOGQyy4hFCA2mlHtnAaf+FeOMxi0fZmfB41ZL+QbFG6BVaZF5IeFIVy53Q== dependencies: - jest-get-type "^26.3.0" - pretty-format "^26.6.2" + jest-get-type "^27.0.1" + pretty-format "^27.0.2" jest-matcher-utils@^22.0.0: version "22.4.3" @@ -5509,15 +5454,15 @@ jest-matcher-utils@^24.9.0: jest-get-type "^24.9.0" pretty-format "^24.9.0" -jest-matcher-utils@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" - integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== +jest-matcher-utils@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.0.2.tgz#f14c060605a95a466cdc759acc546c6f4cbfc4f0" + integrity sha512-Qczi5xnTNjkhcIB0Yy75Txt+Ez51xdhOxsukN7awzq2auZQGPHcQrJ623PZj0ECDEMOk2soxWx05EXdXGd1CbA== dependencies: chalk "^4.0.0" - jest-diff "^26.6.2" - jest-get-type "^26.3.0" - pretty-format "^26.6.2" + jest-diff "^27.0.2" + jest-get-type "^27.0.1" + pretty-format "^27.0.2" jest-message-util@^24.9.0: version "24.9.0" @@ -5533,20 +5478,20 @@ jest-message-util@^24.9.0: slash "^2.0.0" stack-utils "^1.0.1" -jest-message-util@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" - integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== +jest-message-util@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.0.2.tgz#181c9b67dff504d8f4ad15cba10d8b80f272048c" + integrity sha512-rTqWUX42ec2LdMkoUPOzrEd1Tcm+R1KfLOmFK+OVNo4MnLsEaxO5zPDb2BbdSmthdM/IfXxOZU60P/WbWF8BTw== dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/types" "^26.6.2" + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.0.2" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.4" - micromatch "^4.0.2" - pretty-format "^26.6.2" + micromatch "^4.0.4" + pretty-format "^27.0.2" slash "^3.0.0" - stack-utils "^2.0.2" + stack-utils "^2.0.3" jest-mock-extended@1.0.15: version "1.0.15" @@ -5555,12 +5500,12 @@ jest-mock-extended@1.0.15: dependencies: ts-essentials "^4.0.0" -jest-mock@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" - integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== +jest-mock@^27.0.3: + version "27.0.3" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.0.3.tgz#5591844f9192b3335c0dca38e8e45ed297d4d23d" + integrity sha512-O5FZn5XDzEp+Xg28mUz4ovVcdwBBPfAhW9+zJLO0Efn2qNbYcDaJvSlRiQ6BCZUCVOJjALicuJQI9mRFjv1o9Q== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.0.2" "@types/node" "*" jest-pnp-resolver@^1.2.2: @@ -5573,97 +5518,99 @@ jest-regex-util@^24.9.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== -jest-regex-util@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" - integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== +jest-regex-util@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.1.tgz#69d4b1bf5b690faa3490113c47486ed85dd45b68" + integrity sha512-6nY6QVcpTgEKQy1L41P4pr3aOddneK17kn3HJw6SdwGiKfgCGTvH02hVXL0GU8GEKtPH83eD2DIDgxHXOxVohQ== -jest-resolve-dependencies@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" - integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== +jest-resolve-dependencies@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.4.tgz#a07a242d70d668afd3fcf7f4270755eebb1fe579" + integrity sha512-F33UPfw1YGWCV2uxJl7wD6TvcQn5IC0LtguwY3r4L7R6H4twpLkp5Q2ZfzRx9A2I3G8feiy0O0sqcn/Qoym71A== dependencies: - "@jest/types" "^26.6.2" - jest-regex-util "^26.0.0" - jest-snapshot "^26.6.2" + "@jest/types" "^27.0.2" + jest-regex-util "^27.0.1" + jest-snapshot "^27.0.4" -jest-resolve@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" - integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== +jest-resolve@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.0.4.tgz#8a27bc3f2f00c8ea28f3bc99bbf6f468300a703d" + integrity sha512-BcfyK2i3cG79PDb/6gB6zFeFQlcqLsQjGBqznFCpA0L/3l1L/oOsltdUjs5eISAWA9HS9qtj8v2PSZr/yWxONQ== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.0.2" chalk "^4.0.0" + escalade "^3.1.1" graceful-fs "^4.2.4" jest-pnp-resolver "^1.2.2" - jest-util "^26.6.2" - read-pkg-up "^7.0.1" - resolve "^1.18.1" + jest-util "^27.0.2" + jest-validate "^27.0.2" + resolve "^1.20.0" slash "^3.0.0" -jest-runner@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" - integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== +jest-runner@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.0.4.tgz#2787170a9509b792ae129794f6944d27d5d12a4f" + integrity sha512-NfmvSYLCsCJk2AG8Ar2NAh4PhsJJpO+/r+g4bKR5L/5jFzx/indUpnVBdrfDvuqhGLLAvrKJ9FM/Nt8o1dsqxg== dependencies: - "@jest/console" "^26.6.2" - "@jest/environment" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.0.2" + "@jest/environment" "^27.0.3" + "@jest/test-result" "^27.0.2" + "@jest/transform" "^27.0.2" + "@jest/types" "^27.0.2" "@types/node" "*" chalk "^4.0.0" - emittery "^0.7.1" + emittery "^0.8.1" exit "^0.1.2" graceful-fs "^4.2.4" - jest-config "^26.6.3" - jest-docblock "^26.0.0" - jest-haste-map "^26.6.2" - jest-leak-detector "^26.6.2" - jest-message-util "^26.6.2" - jest-resolve "^26.6.2" - jest-runtime "^26.6.3" - jest-util "^26.6.2" - jest-worker "^26.6.2" + jest-docblock "^27.0.1" + jest-environment-jsdom "^27.0.3" + jest-environment-node "^27.0.3" + jest-haste-map "^27.0.2" + jest-leak-detector "^27.0.2" + jest-message-util "^27.0.2" + jest-resolve "^27.0.4" + jest-runtime "^27.0.4" + jest-util "^27.0.2" + jest-worker "^27.0.2" source-map-support "^0.5.6" - throat "^5.0.0" - -jest-runtime@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" - integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== - dependencies: - "@jest/console" "^26.6.2" - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/globals" "^26.6.2" - "@jest/source-map" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/yargs" "^15.0.0" + throat "^6.0.1" + +jest-runtime@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.0.4.tgz#2e4a6aa77cac32ac612dfe12768387a8aa15c2f0" + integrity sha512-voJB4xbAjS/qYPboV+e+gmg3jfvHJJY4CagFWBOM9dQKtlaiTjcpD2tWwla84Z7PtXSQPeIpXY0qksA9Dum29A== + dependencies: + "@jest/console" "^27.0.2" + "@jest/environment" "^27.0.3" + "@jest/fake-timers" "^27.0.3" + "@jest/globals" "^27.0.3" + "@jest/source-map" "^27.0.1" + "@jest/test-result" "^27.0.2" + "@jest/transform" "^27.0.2" + "@jest/types" "^27.0.2" + "@types/yargs" "^16.0.0" chalk "^4.0.0" - cjs-module-lexer "^0.6.0" + cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.3" graceful-fs "^4.2.4" - jest-config "^26.6.3" - jest-haste-map "^26.6.2" - jest-message-util "^26.6.2" - jest-mock "^26.6.2" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" + jest-haste-map "^27.0.2" + jest-message-util "^27.0.2" + jest-mock "^27.0.3" + jest-regex-util "^27.0.1" + jest-resolve "^27.0.4" + jest-snapshot "^27.0.4" + jest-util "^27.0.2" + jest-validate "^27.0.2" slash "^3.0.0" strip-bom "^4.0.0" - yargs "^15.4.1" + yargs "^16.0.3" -jest-serializer@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" - integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== +jest-serializer@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.0.1.tgz#2464d04dcc33fb71dc80b7c82e3c5e8a08cb1020" + integrity sha512-svy//5IH6bfQvAbkAEg1s7xhhgHTtXu0li0I2fdKHDsLP2P2MOiscPQIENQep8oU2g2B3jqLyxKKzotZOz4CwQ== dependencies: "@types/node" "*" graceful-fs "^4.2.4" @@ -5676,29 +5623,37 @@ jest-silent-reporter@0.5.0: chalk "^4.0.0" jest-util "^26.0.0" -jest-snapshot@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" - integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== +jest-snapshot@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.0.4.tgz#2b96e22ca90382b3e93bd0aae2ce4c78bf51fb5b" + integrity sha512-hnjrvpKGdSMvKfbHyaG5Kul7pDJGZvjVy0CKpzhu28MmAssDXS6GpynhXzgst1wBQoKD8c9b2VS2a5yhDLQRCA== dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/parser" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" "@babel/types" "^7.0.0" - "@jest/types" "^26.6.2" + "@jest/transform" "^27.0.2" + "@jest/types" "^27.0.2" "@types/babel__traverse" "^7.0.4" - "@types/prettier" "^2.0.0" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^26.6.2" + expect "^27.0.2" graceful-fs "^4.2.4" - jest-diff "^26.6.2" - jest-get-type "^26.3.0" - jest-haste-map "^26.6.2" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-resolve "^26.6.2" + jest-diff "^27.0.2" + jest-get-type "^27.0.1" + jest-haste-map "^27.0.2" + jest-matcher-utils "^27.0.2" + jest-message-util "^27.0.2" + jest-resolve "^27.0.4" + jest-util "^27.0.2" natural-compare "^1.4.0" - pretty-format "^26.6.2" + pretty-format "^27.0.2" semver "^7.3.2" -jest-util@>=25.1.0, jest-util@^26.0.0, jest-util@^26.1.0, jest-util@^26.6.2: +jest-util@^26.0.0: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== @@ -5710,48 +5665,60 @@ jest-util@>=25.1.0, jest-util@^26.0.0, jest-util@^26.1.0, jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" -jest-validate@>=25.1.0, jest-validate@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" - integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== +jest-util@^27.0.0, jest-util@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.0.2.tgz#fc2c7ace3c75ae561cf1e5fdb643bf685a5be7c7" + integrity sha512-1d9uH3a00OFGGWSibpNYr+jojZ6AckOMCXV2Z4K3YXDnzpkAaXQyIpY14FOJPiUmil7CD+A6Qs+lnnh6ctRbIA== dependencies: - "@jest/types" "^26.6.2" - camelcase "^6.0.0" + "@jest/types" "^27.0.2" + "@types/node" "*" chalk "^4.0.0" - jest-get-type "^26.3.0" + graceful-fs "^4.2.4" + is-ci "^3.0.0" + picomatch "^2.2.3" + +jest-validate@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.0.2.tgz#7fe2c100089449cd5cbb47a5b0b6cb7cda5beee5" + integrity sha512-UgBF6/oVu1ofd1XbaSotXKihi8nZhg0Prm8twQ9uCuAfo59vlxCXMPI/RKmrZEVgi3Nd9dS0I8A0wzWU48pOvg== + dependencies: + "@jest/types" "^27.0.2" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^27.0.1" leven "^3.1.0" - pretty-format "^26.6.2" + pretty-format "^27.0.2" -jest-watcher@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" - integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== +jest-watcher@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.0.2.tgz#dab5f9443e2d7f52597186480731a8c6335c5deb" + integrity sha512-8nuf0PGuTxWj/Ytfw5fyvNn/R80iXY8QhIT0ofyImUvdnoaBdT6kob0GmhXR+wO+ALYVnh8bQxN4Tjfez0JgkA== dependencies: - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/test-result" "^27.0.2" + "@jest/types" "^27.0.2" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^26.6.2" + jest-util "^27.0.2" string-length "^4.0.1" -jest-worker@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== +jest-worker@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.2.tgz#4ebeb56cef48b3e7514552f80d0d80c0129f0b05" + integrity sha512-EoBdilOTTyOgmHXtw/cPc+ZrCA0KJMrkXzkrPGNwLmnvvlN1nj7MPrxpT7m+otSv2e1TLaVffzDnE/LB14zJMg== dependencies: "@types/node" "*" merge-stream "^2.0.0" - supports-color "^7.0.0" + supports-color "^8.0.0" -jest@26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" - integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== +jest@27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.0.4.tgz#91d4d564b36bcf93b98dac1ab19f07089e670f53" + integrity sha512-Px1iKFooXgGSkk1H8dJxxBIrM3tsc5SIuI4kfKYK2J+4rvCvPGr/cXktxh0e9zIPQ5g09kOMNfHQEmusBUf/ZA== dependencies: - "@jest/core" "^26.6.3" + "@jest/core" "^27.0.4" import-local "^3.0.2" - jest-cli "^26.6.3" + jest-cli "^27.0.4" js-tokens@^4.0.0: version "4.0.0" @@ -5778,13 +5745,13 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^16.4.0: - version "16.5.3" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.3.tgz#13a755b3950eb938b4482c407238ddf16f0d2136" - integrity sha512-Qj1H+PEvUsOtdPJ056ewXM4UJPCi4hhLA8wpiz9F2YvsRBhuFsXxtrIFAgGBDynQA9isAMGE91PfUYbdMPXuTA== +jsdom@^16.6.0: + version "16.6.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.6.0.tgz#f79b3786682065492a3da6a60a4695da983805ac" + integrity sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg== dependencies: abab "^2.0.5" - acorn "^8.1.0" + acorn "^8.2.4" acorn-globals "^6.0.0" cssom "^0.4.4" cssstyle "^2.3.0" @@ -5792,12 +5759,13 @@ jsdom@^16.4.0: decimal.js "^10.2.1" domexception "^2.0.1" escodegen "^2.0.0" + form-data "^3.0.0" html-encoding-sniffer "^2.0.1" - is-potential-custom-element-name "^1.0.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" nwsapi "^2.2.0" parse5 "6.0.1" - request "^2.88.2" - request-promise-native "^1.0.9" saxes "^5.0.1" symbol-tree "^3.2.4" tough-cookie "^4.0.0" @@ -5807,7 +5775,7 @@ jsdom@^16.4.0: whatwg-encoding "^1.0.5" whatwg-mimetype "^2.3.0" whatwg-url "^8.5.0" - ws "^7.4.4" + ws "^7.4.5" xml-name-validator "^3.0.0" jsesc@^2.5.1: @@ -5868,10 +5836,10 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= -json-stringify-nice@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.3.tgz#43991531d674ad5c19152d519047849935293add" - integrity sha512-w8+cZZFgcPtFkZTmkA1UpRH0GXXfpeuc/cClNkQjLt9JoQd8cBFSyB8J1WWjJrthIYViHobwnh3jA4z5X2LdGA== +json-stringify-nice@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67" + integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw== json-stringify-pretty-compact@3.0.0: version "3.0.0" @@ -5970,14 +5938,14 @@ levn@~0.3.0: type-check "~0.3.2" libnpmaccess@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-4.0.2.tgz#781832fb7ccb867b26343a75a85ad9c43e50406e" - integrity sha512-avXtJibZuGap0/qADDYqb9zdpgzVu/yG5+tl2sTRa7MCkDNv2ZlGwCYI0r6/+tmqXPj0iB9fKexHz426vB326w== + version "4.0.3" + resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-4.0.3.tgz#dfb0e5b0a53c315a2610d300e46b4ddeb66e7eec" + integrity sha512-sPeTSNImksm8O2b6/pf3ikv4N567ERYEpeKRPSmqlNt1dTZbvgpJIzg5vAhXHpw2ISBsELFRelk0jEahj1c6nQ== dependencies: aproba "^2.0.0" minipass "^3.1.1" npm-package-arg "^8.1.2" - npm-registry-fetch "^10.0.0" + npm-registry-fetch "^11.0.0" libnpmdiff@^2.0.4: version "2.0.4" @@ -5993,10 +5961,10 @@ libnpmdiff@^2.0.4: pacote "^11.3.0" tar "^6.1.0" -libnpmexec@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/libnpmexec/-/libnpmexec-1.1.0.tgz#daa79d4545e21d0eb6887a33e0bbc8ab466099b2" - integrity sha512-OWpsPWtD6CAn66JouyjBfhQ9eS1mAtXgZXXd1SoAyUP3Mol+ao9IJ2THcJQcgX96keVmZkUA11uJS5ZNEd9DwA== +libnpmexec@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/libnpmexec/-/libnpmexec-1.2.0.tgz#ab0294ab63bb599b3ac6921129b7a706d4d56da2" + integrity sha512-LkxnH2wsMUI4thsgUK0r+EFZ5iCjKlp21J68dFY7AzD5uaaIPqO3lqVvYbyl1Umz1R4rY9t3vFa1fF3hzo6Y2Q== dependencies: "@npmcli/arborist" "^2.3.0" "@npmcli/ci-detect" "^1.3.0" @@ -6010,28 +5978,28 @@ libnpmexec@^1.1.0: read-package-json-fast "^2.0.2" walk-up-path "^1.0.0" -libnpmfund@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/libnpmfund/-/libnpmfund-1.0.2.tgz#d9552d4b76dd7f0a1a61b7af6b8f27184e51b0f5" - integrity sha512-Scw2JiLxfT7wqW/VbxIXV8u3FaFT/ZlR8YLFgTdCPsL1Hhli0554ZXyP8JTu1sLeDpHsoqtgLb4mgYVQnqigjA== +libnpmfund@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/libnpmfund/-/libnpmfund-1.1.0.tgz#ee91313905b3194b900530efa339bc3f9fc4e5c4" + integrity sha512-Kfmh3pLS5/RGKG5WXEig8mjahPVOxkik6lsbH4iX0si1xxNi6eeUh/+nF1MD+2cgalsQif3O5qyr6mNz2ryJrQ== dependencies: - "@npmcli/arborist" "^2.0.0" + "@npmcli/arborist" "^2.5.0" libnpmhook@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/libnpmhook/-/libnpmhook-6.0.2.tgz#4a7ad17d7c0f7e0d0e4268c443147938f6bfc534" - integrity sha512-fTw+8i6iyz9v6azSvQEVYxQhAaE2X1eiVMAUqsiwECWeylyc5KUoghHyYg0Kz5jEy9IOTaWYJXc6gMf0rQFpow== + version "6.0.3" + resolved "https://registry.yarnpkg.com/libnpmhook/-/libnpmhook-6.0.3.tgz#1d7f0d7e6a7932fbf7ce0881fdb0ed8bf8748a30" + integrity sha512-3fmkZJibIybzmAvxJ65PeV3NzRc0m4xmYt6scui5msocThbEp4sKFT80FhgrCERYDjlUuFahU6zFNbJDHbQ++g== dependencies: aproba "^2.0.0" - npm-registry-fetch "^10.0.0" + npm-registry-fetch "^11.0.0" libnpmorg@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/libnpmorg/-/libnpmorg-2.0.2.tgz#395344d6b43c0b63a7b03f9e29c191144c0dd7f9" - integrity sha512-FY5Mzd2CblVqLWwhEIuefzdWwZOxYN1Vvk8KnXxrPhXHDijuQqaN9wgxZlsHwdGC02kPoDKkg67mH/ir/W/YLQ== + version "2.0.3" + resolved "https://registry.yarnpkg.com/libnpmorg/-/libnpmorg-2.0.3.tgz#4e605d4113dfa16792d75343824a0625c76703bc" + integrity sha512-JSGl3HFeiRFUZOUlGdiNcUZOsUqkSYrg6KMzvPZ1WVZ478i47OnKSS0vkPmX45Pai5mTKuwIqBMcGWG7O8HfdA== dependencies: aproba "^2.0.0" - npm-registry-fetch "^10.0.0" + npm-registry-fetch "^11.0.0" libnpmpack@^2.0.1: version "2.0.1" @@ -6043,30 +6011,30 @@ libnpmpack@^2.0.1: pacote "^11.2.6" libnpmpublish@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-4.0.1.tgz#08ca2cbb5d7f6be1ce4f3f9c49b3822682bcf166" - integrity sha512-hZCrZ8v4G9YH3DxpIyBdob25ijD5v5LNzRbwsej4pPDopjdcLLj1Widl+BUeFa7D0ble1JYL4F3owjLJqiA8yA== + version "4.0.2" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-4.0.2.tgz#be77e8bf5956131bcb45e3caa6b96a842dec0794" + integrity sha512-+AD7A2zbVeGRCFI2aO//oUmapCwy7GHqPXFJh3qpToSRNU+tXKJ2YFUgjt04LPPAf2dlEH95s6EhIHM1J7bmOw== dependencies: normalize-package-data "^3.0.2" npm-package-arg "^8.1.2" - npm-registry-fetch "^10.0.0" + npm-registry-fetch "^11.0.0" semver "^7.1.3" ssri "^8.0.1" libnpmsearch@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/libnpmsearch/-/libnpmsearch-3.1.1.tgz#418ad6a2587b77b68222210f0a864a96d3841d57" - integrity sha512-XpJpsc7cg7+gdsC5IglXrPjeGzJh+GLhy8yLv4iKPhUFoe1s7dQvhsP5lN7YF0T98WwdEoMkKmbRJRCjEn3pEw== + version "3.1.2" + resolved "https://registry.yarnpkg.com/libnpmsearch/-/libnpmsearch-3.1.2.tgz#aee81b9e4768750d842b627a3051abc89fdc15f3" + integrity sha512-BaQHBjMNnsPYk3Bl6AiOeVuFgp72jviShNBw5aHaHNKWqZxNi38iVNoXbo6bG/Ccc/m1To8s0GtMdtn6xZ1HAw== dependencies: - npm-registry-fetch "^10.0.0" + npm-registry-fetch "^11.0.0" libnpmteam@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/libnpmteam/-/libnpmteam-2.0.3.tgz#dee3f113f9f8901ecbebb49ead13845874ed845d" - integrity sha512-bCNyYddHmvGEfxOYIk5WcdWHXHIygfAo5tmcGf19YyIG42igd0+CckpuXXJgtIAMZSTFhwskWx9YZ9CmWL94CA== + version "2.0.4" + resolved "https://registry.yarnpkg.com/libnpmteam/-/libnpmteam-2.0.4.tgz#9dbe2e18ae3cb97551ec07d2a2daf9944f3edc4c" + integrity sha512-FPrVJWv820FZFXaflAEVTLRWZrerCvfe7ZHSMzJ/62EBlho2KFlYKjyNEsPW3JiV7TLSXi3vo8u0gMwIkXSMTw== dependencies: aproba "^2.0.0" - npm-registry-fetch "^10.0.0" + npm-registry-fetch "^11.0.0" libnpmversion@^1.2.0: version "1.2.0" @@ -6091,16 +6059,6 @@ linkify-it@^3.0.1: dependencies: uc.micro "^1.0.1" -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" - integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" - load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -6148,11 +6106,6 @@ lodash.escaperegexp@^4.1.2: resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c= -lodash.flatten@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" - integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= - lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" @@ -6168,6 +6121,11 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" @@ -6188,7 +6146,7 @@ lodash.uniqby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" integrity sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI= -lodash@4.x, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: +lodash@4.x, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -6227,7 +6185,7 @@ make-error@1.x, make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -make-fetch-happen@^8.0.14, make-fetch-happen@^8.0.9: +make-fetch-happen@^8.0.14: version "8.0.14" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" integrity sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ== @@ -6248,6 +6206,28 @@ make-fetch-happen@^8.0.14, make-fetch-happen@^8.0.9: socks-proxy-agent "^5.0.0" ssri "^8.0.0" +make-fetch-happen@^9.0.1: + version "9.0.2" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.0.2.tgz#aa8c0e4a5e3a5f2be86c54d3abed44fe5a32ad5d" + integrity sha512-UkAWAuXPXSSlVviTjH2We20mtj1NnZW2Qq/oTY2dyMbRQ5CR3Xed3akCDMnM7j6axrMY80lhgM7loNE132PfAw== + dependencies: + agentkeepalive "^4.1.3" + cacache "^15.2.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.2" + promise-retry "^2.0.1" + socks-proxy-agent "^5.0.0" + ssri "^8.0.0" + makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -6349,9 +6329,9 @@ marked-terminal@^4.1.1: supports-hyperlinks "^2.1.0" marked@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/marked/-/marked-2.0.3.tgz#3551c4958c4da36897bda2a16812ef1399c8d6b0" - integrity sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA== + version "2.0.7" + resolved "https://registry.yarnpkg.com/marked/-/marked-2.0.7.tgz#bc5b857a09071b48ce82a1f7304913a993d4b7d1" + integrity sha512-BJXxkuIfJchcXOJWTT2DOL+yFWifFv2yGYOUzvXg8Qz610QKw+sHCvTMYwA+qWGhlA2uivBezChZ/pBy1tWdkQ== matcher@^3.0.0: version "3.0.0" @@ -6464,7 +6444,7 @@ micromark@~2.11.0: debug "^4.0.0" parse-entities "^2.0.0" -micromatch@^3.1.10, micromatch@^3.1.4: +micromatch@^3.1.10: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -6483,7 +6463,7 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2, micromatch@~4.0.2: +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@~4.0.2: version "4.0.4" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== @@ -6491,17 +6471,17 @@ micromatch@^4.0.2, micromatch@~4.0.2: braces "^3.0.1" picomatch "^2.2.3" -mime-db@1.47.0: - version "1.47.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" - integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== +mime-db@1.48.0: + version "1.48.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" + integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.30" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" - integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== + version "2.1.31" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" + integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== dependencies: - mime-db "1.47.0" + mime-db "1.48.0" mime@^2.4.3: version "2.5.2" @@ -6544,7 +6524,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: +minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -6675,7 +6655,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0, ms@^2.1.2: +ms@^2.0.0, ms@^2.1.1, ms@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -6742,6 +6722,11 @@ ncp@~2.0.0: resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= +negotiator@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + neo-async@^2.6.0: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -6757,17 +6742,17 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -nock@13.0.11: - version "13.0.11" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.0.11.tgz#ba733252e720897ca50033205c39db0c7470f331" - integrity sha512-sKZltNkkWblkqqPAsjYW0bm3s9DcHRPiMOyKO/PkfJ+ANHZ2+LA2PLe22r4lLrKgXaiSaDQwW3qGsJFtIpQIeQ== +nock@13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.1.0.tgz#41c8ce8b35ab7d618c4cbf40de1d5bce319979ba" + integrity sha512-3N3DUY8XYrxxzWazQ+nSBpiaJ3q6gcpNh4gXovC/QBxrsvNp4tq+wsLHF6mJ3nrn3lPLn7KCJqKxy/9aD+0fdw== dependencies: debug "^4.1.0" json-stringify-safe "^5.0.1" lodash.set "^4.3.2" propagate "^2.0.0" -node-emoji@1.10.0, node-emoji@^1.10.0: +node-emoji@^1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw== @@ -6796,9 +6781,9 @@ node-gyp@^7.1.0, node-gyp@^7.1.2: which "^2.0.2" node-gyp@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.0.0.tgz#225af2b06b8419ae81f924bf25ae4c167f6378a5" - integrity sha512-Jod6NxyWtcwrpAQe0O/aXOpC5QfncotgtG73dg65z6VW/C6g/G4jiajXQUBIJ8pk/VfM6mBYE9BN/HvudTunUQ== + version "8.1.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.1.0.tgz#81f43283e922d285c886fb0e0f520a7fd431d8c2" + integrity sha512-o2elh1qt7YUp3lkMwY3/l4KF3j/A3fI/Qt4NH+CQQgPJdqGE9y7qnP84cjIWN27Q0jJkrSAhCVDg+wBVNBYdBg== dependencies: env-paths "^2.2.0" glob "^7.1.4" @@ -6811,12 +6796,12 @@ node-gyp@^8.0.0: tar "^6.1.0" which "^2.0.2" -node-html-parser@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-3.3.0.tgz#6009e52c7cde40c43edcc21116f6cf505129196b" - integrity sha512-grr0KoPA0QDR8nROXr01M5LvpX21ncnyI+Z0HO9JyevYzejS80c3J5t/YNRhBGPHp/EI26bo0NZRRDctxNgC+g== +node-html-parser@3.3.5: + version "3.3.5" + resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-3.3.5.tgz#b312174fda86145b9572218955e400a731c60326" + integrity sha512-d4ex19KYXfhi8dYhGaY4/9oCHRSyaxjd4FKvL3Fx/9f6c+qMVgrG1uXgPO3Mucu+I4Wi7P4o4RIq51NHNiWpSQ== dependencies: - css-select "^3.1.2" + css-select "^4.1.3" he "1.2.0" node-int64@^0.4.0: @@ -6829,22 +6814,10 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-notifier@^8.0.0: - version "8.0.2" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" - integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== - dependencies: - growly "^1.3.0" - is-wsl "^2.2.0" - semver "^7.3.2" - shellwords "^0.1.1" - uuid "^8.3.0" - which "^2.0.2" - node-releases@^1.1.71: - version "1.1.71" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" - integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== + version "1.1.73" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" + integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== node.extend@^2.0.0: version "2.0.2" @@ -6881,37 +6854,25 @@ normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: semver "^7.3.4" validate-npm-package-license "^3.0.1" -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" - integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== - normalize-url@^4.1.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" - integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== -normalize-url@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.0.0.tgz#688ba4251cc46350f5adf4e65e14b7113a752684" - integrity sha512-3nv3dKMucKPEXhx/FEtJQR26ksYdyVlLEP9/dYvYwCbLbP6H8ya94IRf+mB93ec+fndv/Ye8SylWfD7jmN6kSA== +normalize-url@^6.0.0, normalize-url@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.0.1.tgz#a4f27f58cf8c7b287b440b8a8201f42d0b00d256" + integrity sha512-VU4pzAuh7Kip71XEmO9aNREYAdMHFGTVj/i+CaTImS8x0i1d3jUZkXhqluy/PRgjPLMgsLQulYY3PJ/aSbSjpQ== -npm-audit-report@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/npm-audit-report/-/npm-audit-report-2.1.4.tgz#b14c4625131fb7bcacc4b1c83842af1f58c92c98" - integrity sha512-Tz7rnfskSdZ0msTzt2mENC/B+H2QI8u0jN0ck7o3zDsQYIQrek/l3MjEc+CARer+64LsVTU6ZIqNuh0X55QPhw== +npm-audit-report@^2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/npm-audit-report/-/npm-audit-report-2.1.5.tgz#a5b8850abe2e8452fce976c8960dd432981737b5" + integrity sha512-YB8qOoEmBhUH1UJgh1xFAv7Jg1d+xoNhsDYiFQlEFThEBui0W1vIz2ZK6FVg4WZjwEdl7uBQlm1jy3MUfyHeEw== dependencies: chalk "^4.0.0" @@ -6934,19 +6895,19 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== -npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.1, npm-package-arg@^8.1.2: - version "8.1.2" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.2.tgz#b868016ae7de5619e729993fbd8d11dc3c52ab62" - integrity sha512-6Eem455JsSMJY6Kpd3EyWE+n5hC+g9bSyHr9K9U2zqZb7+02+hObQ2c0+8iDk/mNF+8r1MhY44WypKJAkySIYA== +npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.1, npm-package-arg@^8.1.2, npm-package-arg@^8.1.4: + version "8.1.4" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.4.tgz#8001cdbc4363997b8ef6c6cf7aaf543c5805879d" + integrity sha512-xLokoCFqj/rPdr3LvcdDL6Kj6ipXGEDHD/QGpzwU6/pibYUOXmp5DBmg76yukFyx4ZDbrXNOTn+BPyd8TD4Jlw== dependencies: hosted-git-info "^4.0.1" semver "^7.3.4" validate-npm-package-name "^3.0.0" npm-packlist@^2.1.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.2.0.tgz#e28bd46be4e107388be3bb95993d0c0802c0c776" - integrity sha512-d3da2MEaYliq7h+PNOHqUhlQjRm0M6gNPi6yHsZYzsCj6bLqUTWCC+JMzW/u9Aaxu8i4F1AA0eJUPUSoFU5izA== + version "2.2.2" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.2.2.tgz#076b97293fa620f632833186a7a8f65aaa6148c8" + integrity sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg== dependencies: glob "^7.1.6" ignore-walk "^3.0.3" @@ -6964,19 +6925,18 @@ npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.0, npm-pick-manifest@^6.1.1: semver "^7.3.4" npm-profile@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/npm-profile/-/npm-profile-5.0.3.tgz#d7f519002f477ebec925cd4b6c02287674f8b09c" - integrity sha512-fZbRtN7JyEPBkdr+xLlj0lQrNI42TKlw/3EvEB7OzrwiUNl4veHsu2u06N2MrF5EiQbNUuZ54156Qr1K4R+91w== + version "5.0.4" + resolved "https://registry.yarnpkg.com/npm-profile/-/npm-profile-5.0.4.tgz#73e5bd1d808edc2c382d7139049cc367ac43161b" + integrity sha512-OKtU7yoAEBOnc8zJ+/uo5E4ugPp09sopo+6y1njPp+W99P8DvQon3BJYmpvyK2Bf1+3YV5LN1bvgXRoZ1LUJBA== dependencies: - npm-registry-fetch "^10.0.0" + npm-registry-fetch "^11.0.0" -npm-registry-fetch@^10.0.0, npm-registry-fetch@^10.1.1: - version "10.1.1" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-10.1.1.tgz#97bc7a0fca5e8f76cc5162185b8de8caa8bea639" - integrity sha512-F6a3l+ffCQ7hvvN16YG5bpm1rPZntCg66PLHDQ1apWJPOCUVHoKnL2w5fqEaTVhp42dmossTyXeR7hTGirfXrg== +npm-registry-fetch@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz#68c1bb810c46542760d62a6a965f85a702d43a76" + integrity sha512-jmlgSxoDNuhAtxUIG6pVwwtz840i994dL14FoNVZisrmZW5kWd63IUTNv1m/hyRSGSqWjCUp/YZlS1BJyNp9XA== dependencies: - lru-cache "^6.0.0" - make-fetch-happen "^8.0.9" + make-fetch-happen "^9.0.1" minipass "^3.1.3" minipass-fetch "^1.3.0" minipass-json-stream "^1.0.1" @@ -6998,13 +6958,6 @@ npm-run-all@4.1.5: shell-quote "^1.6.1" string.prototype.padend "^3.0.0" -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -7018,11 +6971,11 @@ npm-user-validate@^1.0.1: integrity sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw== npm@^7.0.0: - version "7.12.0" - resolved "https://registry.yarnpkg.com/npm/-/npm-7.12.0.tgz#c2fb58bbb4f7bbef339a945a28b7c75117799bdd" - integrity sha512-8Zvas3+1zKtE4uXIxHWRpg1bqGNrOta9RTDZWJ2k+EfOfzOggPQh9N4jHtsrIuGLawXv9xCWyvauke1UWMOMoA== + version "7.16.0" + resolved "https://registry.yarnpkg.com/npm/-/npm-7.16.0.tgz#2325af134d221059ebc245a4ae5b39d24216a2ba" + integrity sha512-UUnKcjS7qFhZT90iZY/ZWz/jwF+rS0fIohDf41T6/SRXEqut0aav+1NkL6g6GqQGpIVBzpZc75BDfpq4PhfXBg== dependencies: - "@npmcli/arborist" "^2.4.2" + "@npmcli/arborist" "^2.6.1" "@npmcli/ci-detect" "^1.2.0" "@npmcli/config" "^2.2.0" "@npmcli/run-script" "^1.8.5" @@ -7031,13 +6984,13 @@ npm@^7.0.0: ansistyles "~0.1.3" archy "~1.0.0" byte-size "^7.0.1" - cacache "^15.0.6" + cacache "^15.2.0" chalk "^4.1.0" chownr "^2.0.0" cli-columns "^3.1.2" cli-table3 "^0.6.0" columnify "~1.5.4" - glob "^7.1.4" + glob "^7.1.7" graceful-fs "^4.2.6" hosted-git-info "^4.0.2" ini "^2.0.0" @@ -7047,8 +7000,8 @@ npm@^7.0.0: leven "^3.1.0" libnpmaccess "^4.0.2" libnpmdiff "^2.0.4" - libnpmexec "^1.1.0" - libnpmfund "^1.0.2" + libnpmexec "^1.2.0" + libnpmfund "^1.1.0" libnpmhook "^6.0.2" libnpmorg "^2.0.2" libnpmpack "^2.0.1" @@ -7056,7 +7009,7 @@ npm@^7.0.0: libnpmsearch "^3.1.1" libnpmteam "^2.0.3" libnpmversion "^1.2.0" - make-fetch-happen "^8.0.14" + make-fetch-happen "^9.0.1" minipass "^3.1.3" minipass-pipeline "^1.2.4" mkdirp "^1.0.4" @@ -7064,11 +7017,11 @@ npm@^7.0.0: ms "^2.1.2" node-gyp "^7.1.2" nopt "^5.0.0" - npm-audit-report "^2.1.4" - npm-package-arg "^8.1.2" + npm-audit-report "^2.1.5" + npm-package-arg "^8.1.4" npm-pick-manifest "^6.1.1" npm-profile "^5.0.3" - npm-registry-fetch "^10.1.1" + npm-registry-fetch "^11.0.0" npm-user-validate "^1.0.1" npmlog "~4.1.2" opener "^1.5.2" @@ -7136,10 +7089,10 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.9.0: - version "1.10.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.2.tgz#b6385a3e2b7cae0b5eafcf90cddf85d128767f30" - integrity sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA== +object-inspect@^1.10.3, object-inspect@^1.9.0: + version "1.10.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" + integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" @@ -7164,14 +7117,13 @@ object.assign@^4.1.0, object.assign@^4.1.2: object-keys "^1.1.1" object.entries@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.3.tgz#c601c7f168b62374541a07ddbd3e2d5e4f7711a6" - integrity sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg== + version "1.1.4" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.4.tgz#43ccf9a50bc5fd5b649d45ab1a579f24e088cafd" + integrity sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA== dependencies: - call-bind "^1.0.0" + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" - has "^1.0.3" + es-abstract "^1.18.2" object.pick@^1.3.0: version "1.3.0" @@ -7180,15 +7132,14 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.3.tgz#eaa8b1e17589f02f698db093f7c62ee1699742ee" - integrity sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw== +object.values@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30" + integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - has "^1.0.3" + es-abstract "^1.18.2" once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" @@ -7367,9 +7318,9 @@ p-try@^2.0.0: integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== pacote@^11.1.11, pacote@^11.2.6, pacote@^11.3.0, pacote@^11.3.1, pacote@^11.3.3: - version "11.3.3" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.3.3.tgz#d7d6091464f77c09691699df2ded13ab906b3e68" - integrity sha512-GQxBX+UcVZrrJRYMK2HoG+gPeSUX/rQhnbPkkGrCYa4n2F/bgClFPaMm0nsdnYrxnmUy85uMHoFXZ0jTD0drew== + version "11.3.4" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.3.4.tgz#c290b790a5cee3082bb8fa223f3f3e2fdf3d0bfc" + integrity sha512-RfahPCunM9GI7ryJV/zY0bWQiokZyLqaSNHXtbNSoLb7bwTvBbJBEyCJ01KWs4j1Gj7GmX8crYXQ1sNX6P2VKA== dependencies: "@npmcli/git" "^2.0.1" "@npmcli/installed-package-contents" "^1.0.6" @@ -7384,7 +7335,7 @@ pacote@^11.1.11, pacote@^11.2.6, pacote@^11.3.0, pacote@^11.3.1, pacote@^11.3.3: npm-package-arg "^8.0.1" npm-packlist "^2.1.4" npm-pick-manifest "^6.0.0" - npm-registry-fetch "^10.0.0" + npm-registry-fetch "^11.0.0" promise-retry "^2.0.1" read-package-json-fast "^2.0.1" rimraf "^3.0.2" @@ -7424,13 +7375,6 @@ parse-entities@^2.0.0: is-decimal "^1.0.0" is-hexadecimal "^1.0.0" -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" - parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" @@ -7467,12 +7411,12 @@ parse-path@^4.0.0: query-string "^6.13.8" parse-url@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.2.tgz#856a3be1fcdf78dc93fc8b3791f169072d898b59" - integrity sha512-Czj+GIit4cdWtxo3ISZCvLiUjErSo0iI3wJ+q9Oi3QuMYTI6OZu+7cewMWZ+C1YAnKhYTk6/TLuhIgCypLthPA== + version "5.0.3" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.3.tgz#c158560f14cb1560917e0b7fd8b01adc1e9d3cab" + integrity sha512-nrLCVMJpqo12X8uUJT4GJPd5AFaTOrGx/QpJy3HNcVtq0AZSstVIsnxS5fqNPuoqMUs3MyfBoOP6Zvu2Arok5A== dependencies: is-ssh "^1.3.0" - normalize-url "^3.3.0" + normalize-url "^6.0.1" parse-path "^4.0.0" protocols "^1.4.0" @@ -7501,7 +7445,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^2.0.0, path-key@^2.0.1: +path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= @@ -7512,16 +7456,9 @@ path-key@^3.0.0, path-key@^3.1.0: integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== - -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" - integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= - dependencies: - pify "^2.0.0" + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-type@^3.0.0: version "3.0.0" @@ -7535,26 +7472,26 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d" - integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg== + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== pidtree@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" @@ -7589,6 +7526,13 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" + integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= + dependencies: + find-up "^2.1.0" + pluralize@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" @@ -7609,10 +7553,10 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prettier@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" - integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== +prettier@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6" + integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA== pretty-bytes@^5.1.0: version "5.6.0" @@ -7647,6 +7591,16 @@ pretty-format@^26.0.0, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" +pretty-format@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.2.tgz#9283ff8c4f581b186b2d4da461617143dca478a4" + integrity sha512-mXKbbBPnYTG7Yra9qFBtqj+IXcsvxsvOBco3QHxtxTl+hHKq6QdzMZ+q0CtL4ORHZgwGImRr2XZUX2EWzORxig== + dependencies: + "@jest/types" "^27.0.2" + ansi-regex "^5.0.0" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-quick@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/pretty-quick/-/pretty-quick-3.1.0.tgz#cb172e9086deb57455dea7c7e8f136cd0a4aef6c" @@ -7878,13 +7832,13 @@ read-package-json@^3.0.1: normalize-package-data "^3.0.0" npm-normalize-package-bin "^1.0.0" -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" - integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= +read-pkg-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= dependencies: find-up "^2.0.0" - read-pkg "^2.0.0" + read-pkg "^3.0.0" read-pkg-up@^7.0.0, read-pkg-up@^7.0.1: version "7.0.1" @@ -7895,15 +7849,6 @@ read-pkg-up@^7.0.0, read-pkg-up@^7.0.1: read-pkg "^5.2.0" type-fest "^0.8.1" -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" - integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= - dependencies: - load-json-file "^2.0.0" - normalize-package-data "^2.3.2" - path-type "^2.0.0" - read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -8024,7 +7969,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexpp@^3.0.0, regexpp@^3.1.0: +regexpp@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== @@ -8068,11 +8013,6 @@ remark@13.0.0: remark-stringify "^9.0.0" unified "^9.1.0" -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - repeat-element@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" @@ -8083,22 +8023,6 @@ repeat-string@^1.0.0, repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -request-promise-core@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" - integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== - dependencies: - lodash "^4.17.19" - -request-promise-native@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" - integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== - dependencies: - request-promise-core "1.1.4" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" @@ -8135,11 +8059,6 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - resolve-alpn@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.2.tgz#30b60cfbb0c0b8dc897940fe13fe255afcdd4d28" @@ -8167,7 +8086,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.20.0: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -8223,11 +8142,6 @@ roarr@^2.15.3: semver-compare "^1.0.0" sprintf-js "^1.1.2" -rsvp@^4.8.4: - version "4.8.5" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" - integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== - run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -8262,21 +8176,6 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sane@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" - integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== - dependencies: - "@cnakazawa/watch" "^1.0.3" - anymatch "^2.0.0" - capture-exit "^2.0.0" - exec-sh "^0.3.2" - execa "^1.0.0" - fb-watchman "^2.0.0" - micromatch "^3.1.4" - minimist "^1.1.1" - walker "~1.0.5" - sax@^1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -8376,7 +8275,7 @@ serialize-error@^7.0.1: dependencies: type-fest "^0.13.1" -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -8429,15 +8328,10 @@ shelljs@0.8.4: interpret "^1.0.0" rechoir "^0.6.2" -shellwords@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" - integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== - -shlex@2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/shlex/-/shlex-2.0.2.tgz#debf51a145f1df9e7cb7cce04340ccb45120092d" - integrity sha512-i4p9nNXgBTILspHwZlBCNsZzwuVWW8SFx5dyIONrjL0R+AbMOPbg7ndqgGfjYivkYRTtZMKqIT8HT+QyOhPQWA== +shlex@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/shlex/-/shlex-2.1.0.tgz#4f8fbf75c4a9956283e4095fa5eac3f4969f6a6b" + integrity sha512-Tk8PjohJbWpGu2NtAlsEi/9AS4GU2zW2ZWLFrWRDskZpSJmyBIU3nTkBtocxD90r3w4BwRevsNtIqIP9HMuYiQ== shortid@2.2.16: version "2.2.16" @@ -8469,10 +8363,10 @@ signale@^1.2.1: figures "^2.0.0" pkg-conf "^2.1.0" -simple-git@2.39.0: - version "2.39.0" - resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-2.39.0.tgz#6320e01675c6ee5272aec00727549a5d2617e35e" - integrity sha512-VOsrmc3fpp1lGVIpo+1SKNqJzrdVJeSGZCeenPKnJPNo5UouAlSkWFc037pfm9wRYtfxBdwp2deVJGCG8J6C8A== +simple-git@2.39.1: + version "2.39.1" + resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-2.39.1.tgz#1d297eb154d30cbfc6edf1133742651838cc5680" + integrity sha512-+kEAkyQHsWejYxQNCzTrjvCxJOcijpB49RSs7HV+TK9B9prUq7YBNpFstQvjfGBn4Hecywp4tm+breGGGHlHwA== dependencies: "@kwsites/file-exists" "^1.1.1" "@kwsites/promise-deferred" "^1.1.1" @@ -8625,9 +8519,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.7" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" - integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== + version "3.0.9" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz#8a595135def9592bda69709474f1cbeea7c2467f" + integrity sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ== split-on-first@^1.0.0: version "1.1.0" @@ -8701,7 +8595,7 @@ stack-utils@^1.0.1: dependencies: escape-string-regexp "^2.0.0" -stack-utils@^2.0.2: +stack-utils@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== @@ -8716,11 +8610,6 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -stealthy-require@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" - integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= - stream-buffers@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521" @@ -8871,11 +8760,6 @@ strip-bom@^4.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" @@ -8912,6 +8796,13 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" @@ -8925,14 +8816,13 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -table@^6.0.4: - version "6.6.0" - resolved "https://registry.yarnpkg.com/table/-/table-6.6.0.tgz#905654b79df98d9e9a973de1dd58682532c40e8e" - integrity sha512-iZMtp5tUvcnAdtHpZTWLPF0M7AgiQsURR2DwmxnJwSy8I3+cY+ozzVvYha3BOLG2TB+L0CqjIz+91htuj6yCXg== +table@^6.0.9: + version "6.7.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" + integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== dependencies: ajv "^8.0.1" lodash.clonedeep "^4.5.0" - lodash.flatten "^4.4.0" lodash.truncate "^4.4.2" slice-ansi "^4.0.0" string-width "^4.2.0" @@ -9004,10 +8894,10 @@ text-table@^0.2.0, text-table@~0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -throat@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" - integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== +throat@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" + integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== through2-concurrent@^2.0.0: version "2.0.0" @@ -9097,14 +8987,6 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -tough-cookie@^2.3.3, tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -9114,10 +8996,18 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + tr46@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479" - integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg== + version "2.1.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== dependencies: punycode "^2.1.1" @@ -9137,20 +9027,15 @@ treeverse@^1.0.4: integrity sha512-whw60l7r+8ZU8Tu/Uc2yxtc4ZTZbR/PF3u1IPNKGQ6p8EICLb3Z2lAgoqw9bqYd8IkgnsaOcLzYHFckjqNsf0g== trim-newlines@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" - integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== trim-off-newlines@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= -trim@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim/-/trim-1.0.1.tgz#68e78f6178ccab9687a610752f4f5e5a7022ee8c" - integrity sha512-3JVP2YVqITUisXblCDq/Bi4P9457G/sdEamInkyvCsjbTcXLXIiG7XCb4kGMFWh6JGXesS3TKxOPtrncN/xe8w== - trough@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" @@ -9161,15 +9046,15 @@ ts-essentials@^4.0.0: resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-4.0.0.tgz#506c42b270bbd0465574b90416533175b09205ab" integrity sha512-uQJX+SRY9mtbKU+g9kl5Fi7AEMofPCvHfJkQlaygpPmHPZrtgaBqbWFOYyiA47RhnSwwnXdepUJrgqUYxoUyhQ== -ts-jest@26.5.6: - version "26.5.6" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.6.tgz#c32e0746425274e1dfe333f43cd3c800e014ec35" - integrity sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA== +ts-jest@27.0.3: + version "27.0.3" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.0.3.tgz#808492f022296cde19390bb6ad627c8126bf93f8" + integrity sha512-U5rdMjnYam9Ucw+h0QvtNDbc5+88nxt7tbIvqaZUhFrfG4+SkWhMXjejCLVGcpILTPuV+H3W/GZDZrnZFpPeXw== dependencies: bs-logger "0.x" buffer-from "1.x" fast-json-stable-stringify "2.x" - jest-util "^26.1.0" + jest-util "^27.0.0" json5 "2.x" lodash "4.x" make-error "1.x" @@ -9218,7 +9103,7 @@ tslib@^2.0.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== -tsutils@^3.17.1: +tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== @@ -9261,10 +9146,10 @@ type-detect@4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.1.3.tgz#ea1a602e98e5a968a56a289886a52f04c686fc81" - integrity sha512-CsiQeFMR1jZEq8R+H59qe+bBevnjoV5N2WZTTdlyqxeoODQOOepN2+msQOywcieDq5sBjabKzTn3U+sfHZlMdw== +type-fest@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.2.0.tgz#4cdf38ef9b047922c26038080cb269752ae359a2" + integrity sha512-++0N6KyAj0t2webXst0PE0xuXb4Dv3z1Z+4SGzK+j/epeWBZCfkQbkW/ezscZwpinmBQ5wu/l4TqagKSVcAGCA== type-fest@^0.13.1: version "0.13.1" @@ -9317,10 +9202,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@4.2.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" - integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== +typescript@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805" + integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" @@ -9328,11 +9213,11 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== uglify-js@^3.1.4: - version "3.13.5" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.5.tgz#5d71d6dbba64cf441f32929b1efce7365bb4f113" - integrity sha512-xtB8yEqIkn7zmOyS2zUNBsYCBRhDkvlNxMMY2smuJ/qA8NCHeQvKCF3i9Z4k8FJH4+PJvZRtMrPynfZ75+CSZw== + version "3.13.9" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.9.tgz#4d8d21dcd497f29cfd8e9378b9df123ad025999b" + integrity sha512-wZbyTQ1w6Y7fHdt8sJnHfSIuWeDgk6B5rCb4E/AM6QNNPbOMIZph21PW5dRB3h7Df0GszN+t7RuUH6sWK5bF0g== -unbox-primitive@^1.0.0: +unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== @@ -9482,12 +9367,12 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -uuid@^3.0.0, uuid@^3.3.2, uuid@^3.3.3: +uuid@^3.0.0, uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.0: +uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -9567,7 +9452,7 @@ walk-up-path@^1.0.0: resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-1.0.0.tgz#d4745e893dd5fd0dbb58dd0a4c6a33d9c9fec53e" integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg== -walker@^1.0.7, walker@~1.0.5: +walker@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= @@ -9623,11 +9508,6 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -9659,15 +9539,6 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -9692,10 +9563,10 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -ws@^7.4.4: - version "7.4.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1" - integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g== +ws@^7.4.5: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== www-authenticate@0.6.2: version "0.6.2" @@ -9736,11 +9607,6 @@ xtend@~4.0.1: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== - y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" @@ -9761,7 +9627,7 @@ yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== -yargs-parser@^18.1.2, yargs-parser@^18.1.3: +yargs-parser@^18.1.3: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -9769,24 +9635,7 @@ yargs-parser@^18.1.2, yargs-parser@^18.1.3: camelcase "^5.0.0" decamelize "^1.2.0" -yargs@^15.4.1: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - -yargs@^16.2.0: +yargs@^16.0.3, yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== @@ -9799,6 +9648,14 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"