diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index e36de07977df81..82f0447930bf9f 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM containerbase/node:14.17.6@sha256:ebe928dcb70c9ec5ad36adc576218d81a3f2a598f0b8cfa14c23467957596389 +FROM containerbase/node:14.17.6@sha256:449af722795fbfec2c8ee00724fea3b1b5a4bbd7bdfecfaf783fa746be48ba19 # renovate: datasource=npm diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 4d53c4b739fea4..8abfac7a137caa 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -719,6 +719,14 @@ If configured, Renovate bypasses its normal major/minor/patch upgrade logic and Beware that Renovate follows tags strictly. For example, if you are following a tag like `next` and then that stream is released as `stable` and `next` is no longer being updated then that means your dependencies also won't be getting updated. +## gitAuthor + +You can customize the Git author that's used whenever Renovate creates a commit. +The `gitAuthor` option accepts a RFC5322-compliant string. + +**Note** We strongly recommend that the Git author email you use is unique to Renovate. +Otherwise, if another bot or human shares the same email and pushes to one of Renovate's branches then Renovate will mistake the branch as unmodified and potentially force push over the changes. + ## gitIgnoredAuthors Specify commit authors ignored by Renovate. @@ -1016,6 +1024,7 @@ Example: This can be a base URL (e.g. `https://api.github.com`) or a hostname like `github.com` or `api.github.com`. If the value starts with `http(s)` then it will only match against URLs which start with the full base URL. Otherwise, it will be matched by checking if the URL's hostname matches the `matchHost` directly or ends with it. +When checking the end of the hostname, a single dot is prefixed to the value of `matchHost`, if one is not already present, to ensure it can only match against whole domain segments. ### timeout diff --git a/docs/usage/java.md b/docs/usage/java.md index e58dbe408117b2..aa8c51072dc721 100644 --- a/docs/usage/java.md +++ b/docs/usage/java.md @@ -16,12 +16,18 @@ Renovate detects versions that are specified in a string `'group:artifact:versio Renovate can update `build.gradle`/`build.gradle.kts` files in the root of the repository. It also updates any `*.gradle`/`*.gradle.kts` files in a subdirectory as multi-project configurations. +Renovate also tries to find updates for dependencies whose version is defined in a `*.properties` file, and scans for `*.versions.toml` files and for `*.toml` files inside the `gradle` folder to keep [catalogs](https://docs.gradle.org/current/userguide/platforms.html) up to date. Renovate does not support: - Projects which do not have either a `build.gradle` or `build.gradle.kts` in the repository root - Android projects that require extra configuration to run (e.g. setting the Android SDK) -- Gradle versions prior to version 5.0. +- Gradle versions older than version 5.0 +- Catalogs defined inside a `build.gradle` or `build.gradle.kts` file rather than in TOML +- Catalogs with version ranges +- Catalogs using the `required`, `strictly`, `preferred`, `reject`, and `rejectAll` version declarations +- Catalogs with custom names that do not end in `.toml` +- Catalogs outside the `gradle` folder whose names do not end in `.versions.toml` ## Gradle Wrapper diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md index 5da83d57cbafe2..387adb6da29c9c 100644 --- a/docs/usage/self-hosted-configuration.md +++ b/docs/usage/self-hosted-configuration.md @@ -205,14 +205,6 @@ This should be set to a Personal Access Token (GitHub only) when `forkMode` is s Renovate will use this token to fork the repository into the personal space of the person owning the Personal Access Token. Renovate will then create branches on the fork and opens Pull Requests on the parent repository. -## gitAuthor - -You can customize the Git author that's used whenever Renovate creates a commit. -The `gitAuthor` option accepts a RFC5322-compliant string. - -**Note** We strongly recommend that the Git author email you use is unique to Renovate. -Otherwise, if another bot or human shares the same email and pushes to one of Renovate's branches then Renovate will mistake the branch as unmodified and potentially force push over the changes. - ## gitNoVerify Controls when Renovate passes the `--no-verify` flag to `git`. diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index ad1a1b846a5cf3..9bb1df4e2795aa 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -634,7 +634,6 @@ const options: RenovateOptions[] = [ name: 'gitAuthor', description: 'Author to use for Git commits. Must conform to RFC5322.', type: 'string', - globalOnly: true, }, { name: 'gitPrivateKey', diff --git a/lib/config/presets/internal/monorepo.ts b/lib/config/presets/internal/monorepo.ts index 0654471b4eafdb..8d8997f3816885 100644 --- a/lib/config/presets/internal/monorepo.ts +++ b/lib/config/presets/internal/monorepo.ts @@ -126,6 +126,7 @@ const repoGroups = { 'https://github.com/neutrinojs/neutrino', 'https://github.com/mozilla-neutrino/neutrino-dev', ], + nexpect: 'https://github.com/fluffynuts/NExpect', nextjs: [ 'https://github.com/zeit/next.js', // old repo 'https://github.com/vercel/next.js', diff --git a/lib/globals.d.ts b/lib/globals.d.ts index eef15fbf1826df..8cd937df0d0887 100644 --- a/lib/globals.d.ts +++ b/lib/globals.d.ts @@ -9,12 +9,6 @@ declare interface Error { validationMessage?: string; } -declare namespace NodeJS { - interface Global { - gitAuthor?: { name: string; email: string }; - } -} - // can't use `resolveJsonModule` because it will copy json files and change dist path declare module '*/package.json' { diff --git a/lib/manager/gradle/index.ts b/lib/manager/gradle/index.ts index 086aab095fdef3..087bf18a9a37ea 100644 --- a/lib/manager/gradle/index.ts +++ b/lib/manager/gradle/index.ts @@ -29,7 +29,12 @@ export function updateDependency( export const language = LANGUAGE_JAVA; export const defaultConfig = { - fileMatch: ['\\.gradle(\\.kts)?$', '(^|/)gradle.properties$'], + fileMatch: [ + '\\.gradle(\\.kts)?$', + '(^|/)gradle.properties$', + '(^|\\/)gradle\\/.+\\.toml$', + '\\.versions\\.toml$', + ], timeout: 600, versioning: gradleVersioning.id, }; diff --git a/lib/manager/gradle/shallow/__fixtures__/1/libs.versions.toml b/lib/manager/gradle/shallow/__fixtures__/1/libs.versions.toml new file mode 100644 index 00000000000000..60a452bda6ada9 --- /dev/null +++ b/lib/manager/gradle/shallow/__fixtures__/1/libs.versions.toml @@ -0,0 +1,17 @@ +[versions] +detekt = "1.17.0" +kotest = "4.6.0" +publish-on-central = "0.5.0" + +[libraries] +detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } +kotest-assertions-core-jvm = { module = "io.kotest:kotest-assertions-core-jvm", version.ref = "kotest" } +kotest-runner-junit5 = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest" } +mockito = { group = "org.mockito", name = "mockito-core", version = "3.10.0" } + +[bundles] +kotest = [ "kotest-runner-junit5", "kotest-assertions-core-jvm" ] + +[plugins] +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +publish-on-central = { id = "org.danilopianini.publish-on-central", version.ref = "publish-on-central" } diff --git a/lib/manager/gradle/shallow/__fixtures__/2/libs.versions.toml b/lib/manager/gradle/shallow/__fixtures__/2/libs.versions.toml new file mode 100644 index 00000000000000..8308a0c81674fa --- /dev/null +++ b/lib/manager/gradle/shallow/__fixtures__/2/libs.versions.toml @@ -0,0 +1,13 @@ +[versions] +kotlin = "1.5.21" +retrofit = "2.8.2" + +[libraries] +okHttp = "com.squareup.okhttp3:okhttp:4.9.0" +okio = { module = "com.squareup.okio:okio", version = "2.8.0" } +picasso = { group = "com.squareup.picasso", name = "picasso", version = "2.5.1" } +retrofit2-retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } + +[plugins] +kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version = "1.5.21" } +kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } diff --git a/lib/manager/gradle/shallow/extract.spec.ts b/lib/manager/gradle/shallow/extract.spec.ts index 1456652cd4e755..55f36525df3f7d 100644 --- a/lib/manager/gradle/shallow/extract.spec.ts +++ b/lib/manager/gradle/shallow/extract.spec.ts @@ -1,5 +1,5 @@ import { extractAllPackageFiles } from '..'; -import { fs } from '../../../../test/util'; +import { fs, loadFixture } from '../../../../test/util'; jest.mock('../../../util/fs'); @@ -136,4 +136,182 @@ describe('manager/gradle/shallow/extract', () => { }, ]); }); + + it('works with dependency catalogs', async () => { + const tomlFile = loadFixture('1/libs.versions.toml'); + const fsMock = { + 'gradle/libs.versions.toml': tomlFile, + }; + mockFs(fsMock); + const res = await extractAllPackageFiles({} as never, Object.keys(fsMock)); + expect(res).toMatchObject([ + { + packageFile: 'gradle/libs.versions.toml', + deps: [ + { + depName: 'io.gitlab.arturbosch.detekt:detekt-formatting', + groupName: 'io.gitlab.arturbosch.detekt', + currentValue: '1.17.0', + managerData: { + fileReplacePosition: 21, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'io.kotest:kotest-assertions-core-jvm', + groupName: 'io.kotest', + currentValue: '4.6.0', + managerData: { + fileReplacePosition: 39, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'io.kotest:kotest-runner-junit5', + groupName: 'io.kotest', + currentValue: '4.6.0', + managerData: { + fileReplacePosition: 39, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'org.mockito:mockito-core', + groupName: 'org.mockito', + currentValue: '3.10.0', + managerData: { + fileReplacePosition: 460, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'io.gitlab.arturbosch.detekt', + depType: 'plugin', + currentValue: '1.17.0', + commitMessageTopic: 'plugin detekt', + lookupName: + 'io.gitlab.arturbosch.detekt:io.gitlab.arturbosch.detekt.gradle.plugin', + managerData: { + fileReplacePosition: 21, + packageFile: 'gradle/libs.versions.toml', + }, + registryUrls: [ + 'https://repo.maven.apache.org/maven2', + 'https://plugins.gradle.org/m2/', + ], + }, + { + depName: 'org.danilopianini.publish-on-central', + depType: 'plugin', + currentValue: '0.5.0', + commitMessageTopic: 'plugin publish-on-central', + lookupName: + 'org.danilopianini.publish-on-central:org.danilopianini.publish-on-central.gradle.plugin', + managerData: { + fileReplacePosition: 68, + packageFile: 'gradle/libs.versions.toml', + }, + registryUrls: [ + 'https://repo.maven.apache.org/maven2', + 'https://plugins.gradle.org/m2/', + ], + }, + ], + }, + ]); + }); + + it("can run Javier's example", async () => { + const tomlFile = loadFixture('2/libs.versions.toml'); + const fsMock = { + 'gradle/libs.versions.toml': tomlFile, + }; + mockFs(fsMock); + const res = await extractAllPackageFiles({} as never, Object.keys(fsMock)); + expect(res).toMatchObject([ + { + packageFile: 'gradle/libs.versions.toml', + deps: [ + { + depName: 'com.squareup.okhttp3:okhttp', + groupName: 'com.squareup.okhttp3', + currentValue: '4.9.0', + managerData: { + fileReplacePosition: 99, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'com.squareup.okio:okio', + groupName: 'com.squareup.okio', + currentValue: '2.8.0', + managerData: { + fileReplacePosition: 161, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'com.squareup.picasso:picasso', + groupName: 'com.squareup.picasso', + currentValue: '2.5.1', + managerData: { + fileReplacePosition: 243, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'com.squareup.retrofit2:retrofit', + groupName: 'com.squareup.retrofit2', + currentValue: '2.8.2', + managerData: { + fileReplacePosition: 41, + packageFile: 'gradle/libs.versions.toml', + }, + }, + { + depName: 'org.jetbrains.kotlin.jvm', + depType: 'plugin', + currentValue: '1.5.21', + commitMessageTopic: 'plugin kotlinJvm', + lookupName: + 'org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin', + managerData: { + fileReplacePosition: 415, + packageFile: 'gradle/libs.versions.toml', + }, + registryUrls: [ + 'https://repo.maven.apache.org/maven2', + 'https://plugins.gradle.org/m2/', + ], + }, + { + depName: 'org.jetbrains.kotlin.plugin.serialization', + depType: 'plugin', + currentValue: '1.5.21', + commitMessageTopic: 'plugin kotlinSerialization', + lookupName: + 'org.jetbrains.kotlin.plugin.serialization:org.jetbrains.kotlin.plugin.serialization.gradle.plugin', + managerData: { + fileReplacePosition: 21, + packageFile: 'gradle/libs.versions.toml', + }, + registryUrls: [ + 'https://repo.maven.apache.org/maven2', + 'https://plugins.gradle.org/m2/', + ], + }, + ], + }, + ]); + }); + + it('ignores an empty TOML', async () => { + const tomlFile = ''; + const fsMock = { + 'gradle/libs.versions.toml': tomlFile, + }; + mockFs(fsMock); + const res = await extractAllPackageFiles({} as never, Object.keys(fsMock)); + expect(res).toBeNull(); + }); }); diff --git a/lib/manager/gradle/shallow/extract.ts b/lib/manager/gradle/shallow/extract.ts index 7dabe5e1ee59d1..cd9a3161f800a5 100644 --- a/lib/manager/gradle/shallow/extract.ts +++ b/lib/manager/gradle/shallow/extract.ts @@ -11,12 +11,14 @@ import type { PackageFile, } from '../../types'; import type { GradleManagerData } from '../types'; +import { parseCatalog } from './extract/catalog'; import { parseGradle, parseProps } from './parser'; import type { PackageVariables, VariableRegistry } from './types'; import { getVars, isGradleFile, isPropsFile, + isTOMLFile, reorderFiles, toAbsolutePath, } from './utils'; @@ -64,6 +66,13 @@ export async function extractAllPackageFiles( const { vars, deps } = parseProps(content, packageFile); updateVars(vars); extractedDeps.push(...deps); + } else if (isTOMLFile(packageFile)) { + try { + const updatesFromCatalog = parseCatalog(packageFile, content); + extractedDeps.push(...updatesFromCatalog); + } catch (error) { + logger.warn({ error }, 'TOML parsing error'); + } } else if (isGradleFile(packageFile)) { const vars = getVars(registry, dir); const { diff --git a/lib/manager/gradle/shallow/extract/catalog.ts b/lib/manager/gradle/shallow/extract/catalog.ts new file mode 100644 index 00000000000000..0812932b95731c --- /dev/null +++ b/lib/manager/gradle/shallow/extract/catalog.ts @@ -0,0 +1,83 @@ +import { parse } from '@iarna/toml'; +import { PackageDependency } from '../../../types'; +import { GradleManagerData } from '../../types'; +import type { GradleCatalog, GradleCatalogPluginDescriptor } from '../types'; + +function findIndexAfter( + content: string, + sliceAfter: string, + find: string +): number { + const slicePoint = content.indexOf(sliceAfter) + sliceAfter.length; + return slicePoint + content.slice(slicePoint).indexOf(find); +} + +export function parseCatalog( + packageFile: string, + content: string +): PackageDependency[] { + const tomlContent = parse(content) as GradleCatalog; + const versions = tomlContent.versions || {}; + const libs = tomlContent.libraries || {}; + const libStartIndex = content.indexOf('libraries'); + const libSubContent = content.slice(libStartIndex); + const versionStartIndex = content.indexOf('versions'); + const versionSubContent = content.slice(versionStartIndex); + const extractedDeps: PackageDependency[] = []; + for (const libraryName of Object.keys(libs)) { + const libDescriptor = libs[libraryName]; + const group: string = + typeof libDescriptor === 'string' + ? libDescriptor.split(':')[0] + : libDescriptor.group || libDescriptor.module?.split(':')[0]; + const name: string = + typeof libDescriptor === 'string' + ? libDescriptor.split(':')[1] + : libDescriptor.name || libDescriptor.module?.split(':')[1]; + const version = libDescriptor.version || libDescriptor.split(':')[2]; + const currentVersion = + typeof version === 'string' ? version : versions[version.ref]; + const fileReplacePosition = + typeof version === 'string' + ? libStartIndex + + findIndexAfter(libSubContent, libraryName, currentVersion) + : versionStartIndex + + findIndexAfter(versionSubContent, version.ref, currentVersion); + const dependency = { + depName: `${group}:${name}`, + groupName: group, + currentValue: currentVersion, + managerData: { fileReplacePosition, packageFile }, + }; + extractedDeps.push(dependency); + } + const plugins = tomlContent.plugins || {}; + const pluginsStartIndex = content.indexOf('[plugins]'); + const pluginsSubContent = content.slice(pluginsStartIndex); + for (const pluginName of Object.keys(plugins)) { + const pluginDescriptor = plugins[ + pluginName + ] as GradleCatalogPluginDescriptor; + const pluginId = pluginDescriptor.id; + const version = pluginDescriptor.version; + const currentVersion: string = + typeof version === 'string' ? version : versions[version.ref]; + const fileReplacePosition = + typeof version === 'string' + ? pluginsStartIndex + + findIndexAfter(pluginsSubContent, pluginId, currentVersion) + : versionStartIndex + + findIndexAfter(versionSubContent, version.ref, currentVersion); + const dependency = { + depType: 'plugin', + depName: pluginId, + lookupName: `${pluginId}:${pluginId}.gradle.plugin`, + registryUrls: ['https://plugins.gradle.org/m2/'], + currentValue: currentVersion, + commitMessageTopic: `plugin ${pluginName}`, + managerData: { fileReplacePosition, packageFile }, + }; + extractedDeps.push(dependency); + } + return extractedDeps; +} diff --git a/lib/manager/gradle/shallow/types.ts b/lib/manager/gradle/shallow/types.ts index 4d71036dbbba37..a1867f968ceaf0 100644 --- a/lib/manager/gradle/shallow/types.ts +++ b/lib/manager/gradle/shallow/types.ts @@ -61,3 +61,32 @@ export interface ParseGradleResult { urls: string[]; vars: PackageVariables; } + +export interface GradleCatalog { + versions?: Map; + libraries?: Map< + string, + GradleCatalogModuleDescriptor | GradleCatalogArtifactDescriptor | string + >; + plugins?: Map; +} + +export interface GradleCatalogModuleDescriptor { + module: string; + version: string | VersionPointer; +} + +export interface GradleCatalogArtifactDescriptor { + name: string; + group: string; + version: string | VersionPointer; +} + +export interface GradleCatalogPluginDescriptor { + id: string; + version: string | VersionPointer; +} + +export interface VersionPointer { + ref: string; +} diff --git a/lib/manager/gradle/shallow/utils.ts b/lib/manager/gradle/shallow/utils.ts index 2d8080ef1a4367..75bf22405d4c56 100644 --- a/lib/manager/gradle/shallow/utils.ts +++ b/lib/manager/gradle/shallow/utils.ts @@ -81,6 +81,11 @@ export function isPropsFile(path: string): boolean { return filename === 'gradle.properties'; } +export function isTOMLFile(path: string): boolean { + const filename = upath.basename(path).toLowerCase(); + return filename.endsWith('.toml'); +} + export function toAbsolutePath(packageFile: string): string { return upath.join(packageFile.replace(/^[/\\]*/, '/')); } diff --git a/lib/manager/kustomize/__fixtures__/depsInResources.yaml b/lib/manager/kustomize/__fixtures__/depsInResources.yaml index acdcb457a67de3..d8c210bd9fb523 100644 --- a/lib/manager/kustomize/__fixtures__/depsInResources.yaml +++ b/lib/manager/kustomize/__fixtures__/depsInResources.yaml @@ -10,5 +10,8 @@ resources: - deployment.yaml - github.com/fluxcd/flux/deploy?ref=1.19.0 +components: +- github.com/fluxcd/flux/memcache-dep?ref=1.18.0 + images: - name: nginx diff --git a/lib/manager/kustomize/__snapshots__/extract.spec.ts.snap b/lib/manager/kustomize/__snapshots__/extract.spec.ts.snap index ec2fc9f8161ef3..55eaee54613589 100644 --- a/lib/manager/kustomize/__snapshots__/extract.spec.ts.snap +++ b/lib/manager/kustomize/__snapshots__/extract.spec.ts.snap @@ -67,7 +67,7 @@ Array [ ] `; -exports[`manager/kustomize/extract extractPackageFile() should extract bases from bases block and the resources block 1`] = ` +exports[`manager/kustomize/extract extractPackageFile() should extract bases resources and components from their respective blocks 1`] = ` Array [ Object { "currentValue": "v0.0.1", @@ -79,6 +79,11 @@ Array [ "datasource": "github-tags", "depName": "fluxcd/flux", }, + Object { + "currentValue": "1.18.0", + "datasource": "github-tags", + "depName": "fluxcd/flux", + }, ] `; diff --git a/lib/manager/kustomize/extract.spec.ts b/lib/manager/kustomize/extract.spec.ts index 7fe27a25229599..44c2b71ca9c099 100644 --- a/lib/manager/kustomize/extract.spec.ts +++ b/lib/manager/kustomize/extract.spec.ts @@ -242,14 +242,16 @@ describe('manager/kustomize/extract', () => { it('does nothing with kustomize empty kustomize files', () => { expect(extractPackageFile(kustomizeEmpty)).toBeNull(); }); - it('should extract bases from bases block and the resources block', () => { + it('should extract bases resources and components from their respective blocks', () => { const res = extractPackageFile(kustomizeDepsInResources); expect(res).not.toBeNull(); expect(res.deps).toMatchSnapshot(); - expect(res.deps).toHaveLength(2); + expect(res.deps).toHaveLength(3); expect(res.deps[0].currentValue).toEqual('v0.0.1'); expect(res.deps[1].currentValue).toEqual('1.19.0'); + expect(res.deps[2].currentValue).toEqual('1.18.0'); expect(res.deps[1].depName).toEqual('fluxcd/flux'); + expect(res.deps[2].depName).toEqual('fluxcd/flux'); }); it('extracts sha256 instead of tag', () => { expect(extractPackageFile(sha)).toMatchSnapshot({ diff --git a/lib/manager/kustomize/extract.ts b/lib/manager/kustomize/extract.ts index 12bfb8280c0d8e..8a06c3da71caf7 100644 --- a/lib/manager/kustomize/extract.ts +++ b/lib/manager/kustomize/extract.ts @@ -84,7 +84,10 @@ export function parseKustomize(content: string): Kustomize | null { return null; } - pkg.bases = (pkg.bases || []).concat(pkg.resources || []); + pkg.bases = (pkg.bases || []).concat( + pkg.resources || [], + pkg.components || [] + ); pkg.images = pkg.images || []; return pkg; diff --git a/lib/manager/kustomize/readme.md b/lib/manager/kustomize/readme.md index b38948bb55f8db..1a63c7718cebf2 100644 --- a/lib/manager/kustomize/readme.md +++ b/lib/manager/kustomize/readme.md @@ -2,6 +2,7 @@ This package will manage two parts of the `kustomization.yaml` file: 1. [remote bases](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/remoteBuild.md) 2. [image tags](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/image.md) +3. [components](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/components.md) **How It Works** diff --git a/lib/platform/azure/index.ts b/lib/platform/azure/index.ts index 4a2ffad09a389d..e91aa5e06ec510 100644 --- a/lib/platform/azure/index.ts +++ b/lib/platform/azure/index.ts @@ -180,8 +180,6 @@ export async function initRepo({ ...config, url, extraCloneOpts: getStorageExtraCloneOpts(opts), - gitAuthorName: global.gitAuthor?.name, - gitAuthorEmail: global.gitAuthor?.email, cloneSubmodules, }); const repoConfig: RepoResult = { diff --git a/lib/platform/bitbucket-server/index.spec.ts b/lib/platform/bitbucket-server/index.spec.ts index 68628b2ed26268..a42764fe0ed5c5 100644 --- a/lib/platform/bitbucket-server/index.spec.ts +++ b/lib/platform/bitbucket-server/index.spec.ts @@ -1311,18 +1311,13 @@ describe('platform/bitbucket-server/index', () => { .twice() .reply(200, { conflicted: false }); - const author = global.gitAuthor; - try { - expect(await bitbucket.getPr(3)).toMatchSnapshot(); + expect(await bitbucket.getPr(3)).toMatchSnapshot(); - expect(await bitbucket.getPr(5)).toMatchSnapshot(); + expect(await bitbucket.getPr(5)).toMatchSnapshot(); - expect(await bitbucket.getPr(5)).toMatchSnapshot(); + expect(await bitbucket.getPr(5)).toMatchSnapshot(); - expect(httpMock.getTrace()).toMatchSnapshot(); - } finally { - global.gitAuthor = author; - } + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('gets a closed PR', async () => { diff --git a/lib/platform/bitbucket-server/index.ts b/lib/platform/bitbucket-server/index.ts index 81ce30222124b0..2bc866b66464fd 100644 --- a/lib/platform/bitbucket-server/index.ts +++ b/lib/platform/bitbucket-server/index.ts @@ -214,8 +214,6 @@ export async function initRepo({ await git.initRepo({ ...config, url: gitUrl, - gitAuthorName: global.gitAuthor?.name, - gitAuthorEmail: global.gitAuthor?.email, cloneSubmodules, }); diff --git a/lib/platform/bitbucket/index.spec.ts b/lib/platform/bitbucket/index.spec.ts index 1226847d3d8dbd..4716c77ab5e5f7 100644 --- a/lib/platform/bitbucket/index.spec.ts +++ b/lib/platform/bitbucket/index.spec.ts @@ -710,7 +710,6 @@ describe('platform/bitbucket/index', () => { it('canRebase', async () => { expect.assertions(4); - const author = global.gitAuthor; const scope = await initRepoMock(); scope .get('/2.0/repositories/some/repo/pullrequests/3') @@ -731,22 +730,13 @@ describe('platform/bitbucket/index', () => { .get('/2.0/repositories/some/repo/pullrequests/5/diff') .twice() .reply(200, diff); - try { - expect(await bitbucket.getPr(3)).toMatchSnapshot(); - - global.gitAuthor = { - email: 'renovate@whitesourcesoftware.com', - name: 'bot', - }; - expect(await bitbucket.getPr(5)).toMatchSnapshot(); - - global.gitAuthor = { email: 'jane@example.com', name: 'jane' }; - expect(await bitbucket.getPr(5)).toMatchSnapshot(); - - expect(httpMock.getTrace()).toMatchSnapshot(); - } finally { - global.gitAuthor = author; - } + expect(await bitbucket.getPr(3)).toMatchSnapshot(); + + expect(await bitbucket.getPr(5)).toMatchSnapshot(); + + expect(await bitbucket.getPr(5)).toMatchSnapshot(); + + expect(httpMock.getTrace()).toMatchSnapshot(); }); }); diff --git a/lib/platform/bitbucket/index.ts b/lib/platform/bitbucket/index.ts index 3837829b4265ba..d9df2f0870e30f 100644 --- a/lib/platform/bitbucket/index.ts +++ b/lib/platform/bitbucket/index.ts @@ -179,8 +179,6 @@ export async function initRepo({ await git.initRepo({ ...config, url, - gitAuthorName: global.gitAuthor?.name, - gitAuthorEmail: global.gitAuthor?.email, cloneSubmodules, }); const repoConfig: RepoResult = { diff --git a/lib/platform/gitea/index.spec.ts b/lib/platform/gitea/index.spec.ts index aa3f41b66b352e..05b9a3de361c3d 100644 --- a/lib/platform/gitea/index.spec.ts +++ b/lib/platform/gitea/index.spec.ts @@ -176,8 +176,6 @@ describe('platform/gitea/index', () => { gitvcs.isBranchStale.mockResolvedValue(false); gitvcs.getBranchCommit.mockReturnValue(mockCommitHash); - global.gitAuthor = { name: 'Renovate', email: 'renovate@example.com' }; - setBaseUrl('https://gitea.renovatebot.com/api/v1'); }); @@ -562,7 +560,9 @@ describe('platform/gitea/index', () => { partial({ commit: { id: mockCommitHash, - author: partial({ email: global.gitAuthor.email }), + author: partial({ + email: 'renovate@whitesourcesoftware.com', + }), }, }) ); diff --git a/lib/platform/gitea/index.ts b/lib/platform/gitea/index.ts index 717e72ed9ced4c..c6f34b19bd1c19 100644 --- a/lib/platform/gitea/index.ts +++ b/lib/platform/gitea/index.ts @@ -297,8 +297,6 @@ const platform: Platform = { await git.initRepo({ ...config, url: URL.format(gitEndpoint), - gitAuthorName: global.gitAuthor?.name, - gitAuthorEmail: global.gitAuthor?.email, }); // Reset cached resources diff --git a/lib/platform/github/index.spec.ts b/lib/platform/github/index.spec.ts index 704c99a4329447..b86436f7534d0c 100644 --- a/lib/platform/github/index.spec.ts +++ b/lib/platform/github/index.spec.ts @@ -30,7 +30,6 @@ describe('platform/github/index', () => { git.getBranchCommit.mockReturnValue( '0d9c7726c3d628b7e28af234595cfd20febdbf8e' ); - delete global.gitAuthor; hostRules.find.mockReturnValue({ token: 'abc123', }); @@ -1887,10 +1886,6 @@ describe('platform/github/index', () => { const scope = httpMock.scope(githubApiHost); initRepoMock(scope, 'some/repo'); scope.post('/graphql').reply(200, graphqlOpenPullRequests); - global.gitAuthor = { - name: 'Renovate Bot', - email: 'renovate@whitesourcesoftware.com', - }; await github.initRepo({ repository: 'some/repo', } as any); diff --git a/lib/platform/github/index.ts b/lib/platform/github/index.ts index 684889bed082cd..19b4fde8da2374 100644 --- a/lib/platform/github/index.ts +++ b/lib/platform/github/index.ts @@ -420,8 +420,6 @@ export async function initRepo({ await git.initRepo({ ...config, url, - gitAuthorName: global.gitAuthor?.name, - gitAuthorEmail: global.gitAuthor?.email, }); const repoConfig: RepoResult = { defaultBranch: config.defaultBranch, diff --git a/lib/platform/gitlab/__snapshots__/index.spec.ts.snap b/lib/platform/gitlab/__snapshots__/index.spec.ts.snap index 8c4cccc473a87a..525b409e9a7e8d 100644 --- a/lib/platform/gitlab/__snapshots__/index.spec.ts.snap +++ b/lib/platform/gitlab/__snapshots__/index.spec.ts.snap @@ -2653,63 +2653,12 @@ Array [ ] `; -exports[`platform/gitlab/index initRepo should use ssh_url_to_repo if gitUrl is set to ssh 1`] = ` -Array [ - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Bearer abc123", - "host": "gitlab.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://gitlab.com/api/v4/projects/some%2Frepo%2Fproject", - }, -] -`; - -exports[`platform/gitlab/index initRepo should use ssh_url_to_repo if gitUrl is set to ssh 2`] = ` -Array [ - Array [ - Object { - "cloneSubmodules": undefined, - "defaultBranch": "master", - "gitAuthorEmail": undefined, - "gitAuthorName": undefined, - "ignorePrAuthor": undefined, - "mergeMethod": "merge", - "repository": "some%2Frepo%2Fproject", - "url": "ssh://git@gitlab.com/some%2Frepo%2Fproject.git", - }, - ], -] -`; - -exports[`platform/gitlab/index initRepo should throw if ssh_url_to_repo is not present but gitUrl is set to ssh 1`] = ` -Array [ - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Bearer abc123", - "host": "gitlab.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://gitlab.com/api/v4/projects/some%2Frepo%2Fproject", - }, -] -`; - 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", @@ -2869,6 +2818,53 @@ Array [ ] `; +exports[`platform/gitlab/index initRepo should throw if ssh_url_to_repo is not present but gitUrl is set to ssh 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer abc123", + "host": "gitlab.com", + "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", + }, + "method": "GET", + "url": "https://gitlab.com/api/v4/projects/some%2Frepo%2Fproject", + }, +] +`; + +exports[`platform/gitlab/index initRepo should use ssh_url_to_repo if gitUrl is set to ssh 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer abc123", + "host": "gitlab.com", + "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", + }, + "method": "GET", + "url": "https://gitlab.com/api/v4/projects/some%2Frepo%2Fproject", + }, +] +`; + +exports[`platform/gitlab/index initRepo should use ssh_url_to_repo if gitUrl is set to ssh 2`] = ` +Array [ + Array [ + Object { + "cloneSubmodules": undefined, + "defaultBranch": "master", + "ignorePrAuthor": undefined, + "mergeMethod": "merge", + "repository": "some%2Frepo%2Fproject", + "url": "ssh://git@gitlab.com/some%2Frepo%2Fproject.git", + }, + ], +] +`; + exports[`platform/gitlab/index massageMarkdown(input) returns updated pr body 1`] = ` "https://github.com/foo/bar/issues/5 plus also [a link](https://github.com/foo/bar/issues/5 diff --git a/lib/platform/gitlab/index.ts b/lib/platform/gitlab/index.ts index 012c04854d2371..3a545f26610284 100644 --- a/lib/platform/gitlab/index.ts +++ b/lib/platform/gitlab/index.ts @@ -286,8 +286,6 @@ export async function initRepo({ await git.initRepo({ ...config, url, - gitAuthorName: global.gitAuthor?.name, - gitAuthorEmail: global.gitAuthor?.email, }); } catch (err) /* istanbul ignore next */ { logger.debug({ err }, 'Caught initRepo error'); diff --git a/lib/platform/index.spec.ts b/lib/platform/index.spec.ts index 54fd78d40abe1b..1a29be4909fd66 100644 --- a/lib/platform/index.spec.ts +++ b/lib/platform/index.spec.ts @@ -70,31 +70,4 @@ describe('platform/index', () => { platform: PLATFORM_TYPE_BITBUCKET, }); }); - - it('returns null if empty email given', () => { - expect(platform.parseGitAuthor(undefined)).toBeNull(); - }); - it('parses bot email', () => { - // FIXME: explicit assert condition - expect( - platform.parseGitAuthor('some[bot]@users.noreply.github.com') - ).toMatchSnapshot(); - }); - it('parses bot name and email', () => { - // FIXME: explicit assert condition - expect( - platform.parseGitAuthor( - '"some[bot]" ' - ) - ).toMatchSnapshot(); - }); - it('escapes names', () => { - // FIXME: explicit assert condition - expect( - platform.parseGitAuthor('name [what] ').name - ).toMatchSnapshot(); - }); - it('gives up', () => { - expect(platform.parseGitAuthor('a.b.c')).toBeNull(); - }); }); diff --git a/lib/platform/index.ts b/lib/platform/index.ts index 8268aecdc7c1da..8780d11ecce4e8 100644 --- a/lib/platform/index.ts +++ b/lib/platform/index.ts @@ -1,10 +1,9 @@ import URL from 'url'; -import addrs from 'email-addresses'; import type { AllConfig } from '../config/types'; import { PLATFORM_NOT_FOUND } from '../constants/error-messages'; import { logger } from '../logger'; import type { HostRule } from '../types'; -import { setNoVerify, setPrivateKey } from '../util/git'; +import { setGitAuthor, setNoVerify, setPrivateKey } from '../util/git'; import * as hostRules from '../util/host-rules'; import platforms from './api'; import type { Platform } from './types'; @@ -39,48 +38,6 @@ export function setPlatformApi(name: string): void { _platform = platforms.get(name); } -interface GitAuthor { - name?: string; - address?: string; -} - -export function parseGitAuthor(input: string): GitAuthor | null { - let result: GitAuthor = null; - if (!input) { - return null; - } - try { - result = addrs.parseOneAddress(input); - if (result) { - return result; - } - if (input.includes('[bot]@')) { - // invalid github app/bot addresses - const parsed = addrs.parseOneAddress( - input.replace('[bot]@', '@') - ) as addrs.ParsedMailbox; - if (parsed?.address) { - result = { - name: parsed.name || input.replace(/@.*/, ''), - address: parsed.address.replace('@', '[bot]@'), - }; - return result; - } - } - if (input.includes('<') && input.includes('>')) { - // try wrapping the name part in quotations - result = addrs.parseOneAddress('"' + input.replace(/(\s?<)/, '"$1')); - if (result) { - return result; - } - } - } catch (err) /* istanbul ignore next */ { - logger.error({ err }, 'Unknown error parsing gitAuthor'); - } - // give up - return null; -} - export async function initPlatform(config: AllConfig): Promise { setPrivateKey(config.gitPrivateKey); setNoVerify(config.gitNoVerify ?? []); @@ -88,30 +45,16 @@ export async function initPlatform(config: AllConfig): Promise { // TODO: types const platformInfo = await platform.initPlatform(config); const returnConfig: any = { ...config, ...platformInfo }; - let gitAuthor: string; // istanbul ignore else if (config?.gitAuthor) { logger.debug(`Using configured gitAuthor (${config.gitAuthor})`); - gitAuthor = config.gitAuthor; + returnConfig.gitAuthor = config.gitAuthor; } else if (platformInfo?.gitAuthor) { logger.debug(`Using platform gitAuthor: ${String(platformInfo.gitAuthor)}`); - gitAuthor = platformInfo.gitAuthor; - } else { - logger.debug( - 'Using default gitAuthor: Renovate Bot ' - ); - gitAuthor = 'Renovate Bot '; + returnConfig.gitAuthor = platformInfo.gitAuthor; } - const gitAuthorParsed = parseGitAuthor(gitAuthor); - // istanbul ignore if - if (!gitAuthorParsed) { - throw new Error('Init: gitAuthor is not parsed as valid RFC5322 format'); - } - global.gitAuthor = { - name: gitAuthorParsed.name, - email: gitAuthorParsed.address, - }; - + // This is done for validation and will be overridden later once repo config is incorporated + setGitAuthor(returnConfig.gitAuthor); const platformRule: HostRule = { hostType: returnConfig.platform, matchHost: URL.parse(returnConfig.endpoint).hostname, diff --git a/lib/platform/__snapshots__/index.spec.ts.snap b/lib/util/git/__snapshots__/author.spec.ts.snap similarity index 50% rename from lib/platform/__snapshots__/index.spec.ts.snap rename to lib/util/git/__snapshots__/author.spec.ts.snap index 3217d7d515f0db..844b8e8eb738a1 100644 --- a/lib/platform/__snapshots__/index.spec.ts.snap +++ b/lib/util/git/__snapshots__/author.spec.ts.snap @@ -1,15 +1,15 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`platform/index escapes names 1`] = `"name [what]"`; +exports[`util/git/author parseGitAuthor escapes names 1`] = `"name [what]"`; -exports[`platform/index parses bot email 1`] = ` +exports[`util/git/author parseGitAuthor parses bot email 1`] = ` Object { "address": "some[bot]@users.noreply.github.com", "name": "some[bot]", } `; -exports[`platform/index parses bot name and email 1`] = ` +exports[`util/git/author parseGitAuthor parses bot name and email 1`] = ` Object { "address": "some[bot]@users.noreply.github.com", "name": "some[bot]", diff --git a/lib/util/git/author.spec.ts b/lib/util/git/author.spec.ts new file mode 100644 index 00000000000000..cd80f6a393fcec --- /dev/null +++ b/lib/util/git/author.spec.ts @@ -0,0 +1,30 @@ +import { parseGitAuthor } from './author'; + +describe('util/git/author', () => { + describe('parseGitAuthor', () => { + it('returns null if empty email given', () => { + expect(parseGitAuthor(undefined)).toBeNull(); + }); + it('parses bot email', () => { + // FIXME: explicit assert condition + expect( + parseGitAuthor('some[bot]@users.noreply.github.com') + ).toMatchSnapshot(); + }); + it('parses bot name and email', () => { + // FIXME: explicit assert condition + expect( + parseGitAuthor('"some[bot]" ') + ).toMatchSnapshot(); + }); + it('escapes names', () => { + // FIXME: explicit assert condition + expect( + parseGitAuthor('name [what] ').name + ).toMatchSnapshot(); + }); + it('gives up', () => { + expect(parseGitAuthor('a.b.c')).toBeNull(); + }); + }); +}); diff --git a/lib/util/git/author.ts b/lib/util/git/author.ts new file mode 100644 index 00000000000000..bb149346afc7fe --- /dev/null +++ b/lib/util/git/author.ts @@ -0,0 +1,44 @@ +import addrs from 'email-addresses'; +import { logger } from '../../logger'; + +export interface GitAuthor { + name?: string; + address?: string; +} + +export function parseGitAuthor(input: string): GitAuthor | null { + let result: GitAuthor = null; + if (!input) { + return null; + } + try { + result = addrs.parseOneAddress(input); + if (result) { + return result; + } + if (input.includes('[bot]@')) { + // invalid github app/bot addresses + const parsed = addrs.parseOneAddress( + input.replace('[bot]@', '@') + ) as addrs.ParsedMailbox; + if (parsed?.address) { + result = { + name: parsed.name || input.replace(/@.*/, ''), + address: parsed.address.replace('@', '[bot]@'), + }; + return result; + } + } + if (input.includes('<') && input.includes('>')) { + // try wrapping the name part in quotations + result = addrs.parseOneAddress('"' + input.replace(/(\s?<)/, '"$1')); + if (result) { + return result; + } + } + } catch (err) /* istanbul ignore next */ { + logger.debug({ err }, 'Unknown error parsing gitAuthor'); + } + // give up + return null; +} diff --git a/lib/util/git/index.spec.ts b/lib/util/git/index.spec.ts index c9232b60f58fa9..36a3136e7453b0 100644 --- a/lib/util/git/index.spec.ts +++ b/lib/util/git/index.spec.ts @@ -3,6 +3,7 @@ import Git from 'simple-git'; import SimpleGit from 'simple-git/src/git'; import tmp from 'tmp-promise'; import { setGlobalConfig } from '../../config/global'; +import { CONFIG_VALIDATION } from '../../constants/error-messages'; import * as git from '.'; import { GitNoVerifyOption, setNoVerify } from '.'; @@ -73,10 +74,9 @@ describe('util/git/index', () => { setGlobalConfig({ localDir: tmpDir.path }); await git.initRepo({ url: origin.path, - gitAuthorName: 'Jest', - gitAuthorEmail: 'Jest@example.com', }); await git.setUserRepoConfig({ branchPrefix: 'renovate/' }); + git.setGitAuthor('Jest '); setNoVerify([]); await git.syncGit(); // override some local git settings for better testing @@ -548,4 +548,9 @@ describe('util/git/index', () => { expect(res).toBe('test-extra-config-value'); }); }); + describe('setGitAuthor()', () => { + it('throws for invalid', () => { + expect(() => git.setGitAuthor('invalid')).toThrow(CONFIG_VALIDATION); + }); + }); }); diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts index e24c8084122bcc..70459d0db36dbb 100644 --- a/lib/util/git/index.ts +++ b/lib/util/git/index.ts @@ -9,7 +9,6 @@ import Git, { TaskOptions, } from 'simple-git'; import { join } from 'upath'; -import { configFileNames } from '../../config/app-strings'; import { getGlobalConfig } from '../../config/global'; import type { RenovateConfig } from '../../config/types'; import { @@ -24,6 +23,7 @@ import { logger } from '../../logger'; import { ExternalHostError } from '../../types/errors/external-host-error'; import { GitOptions, GitProtocol } from '../../types/git'; import { Limit, incLimitedValue } from '../../workers/global/limits'; +import { parseGitAuthor } from './author'; import { GitNoVerifyOption, getNoVerify } from './config'; import { configSigningKey, writePrivateKey } from './private-key'; @@ -44,8 +44,6 @@ interface StorageConfig { currentBranch?: string; url: string; extraCloneOpts?: GitOptions; - gitAuthorName?: string; - gitAuthorEmail?: string; cloneSubmodules?: boolean; } @@ -57,6 +55,8 @@ interface LocalConfig extends StorageConfig { branchIsModified: Record; branchPrefix: string; ignoredAuthors: string[]; + gitAuthorName?: string; + gitAuthorEmail?: string; } // istanbul ignore next @@ -234,12 +234,50 @@ async function setBranchPrefix(branchPrefix: string): Promise { } } +export function setGitAuthor(gitAuthor: string): void { + const gitAuthorParsed = parseGitAuthor( + gitAuthor || 'Renovate Bot ' + ); + if (!gitAuthorParsed) { + const error = new Error(CONFIG_VALIDATION); + error.validationSource = 'None'; + error.validationError = 'Invalid gitAuthor'; + error.validationMessage = `gitAuthor is not parsed as valid RFC5322 format: ${gitAuthor}`; + throw error; + } + config.gitAuthorName = gitAuthorParsed.name; + config.gitAuthorEmail = gitAuthorParsed.address; +} + +export async function writeGitAuthor(): Promise { + const { gitAuthorName, gitAuthorEmail } = config; + try { + if (gitAuthorName) { + logger.debug({ gitAuthorName }, 'Setting git author name'); + await git.addConfig('user.name', gitAuthorName); + } + if (gitAuthorEmail) { + logger.debug({ gitAuthorEmail }, 'Setting git author email'); + await git.addConfig('user.email', gitAuthorEmail); + } + } catch (err) /* istanbul ignore next */ { + checkForPlatformFailure(err); + logger.debug( + { err, gitAuthorName, gitAuthorEmail }, + 'Error setting git author config' + ); + throw new Error(TEMPORARY_ERROR); + } +} + export async function setUserRepoConfig({ branchPrefix, gitIgnoredAuthors, + gitAuthor, }: RenovateConfig): Promise { await setBranchPrefix(branchPrefix); config.ignoredAuthors = gitIgnoredAuthors ?? []; + setGitAuthor(gitAuthor); } export async function getSubmodules(): Promise { @@ -344,21 +382,6 @@ export async function syncGit(): Promise { } logger.warn({ err }, 'Cannot retrieve latest commit'); } - try { - const { gitAuthorName, gitAuthorEmail } = config; - if (gitAuthorName) { - logger.debug({ gitAuthorName }, 'Setting git author name'); - await git.addConfig('user.name', gitAuthorName); - } - if (gitAuthorEmail) { - logger.debug({ gitAuthorEmail }, 'Setting git author email'); - await git.addConfig('user.email', gitAuthorEmail); - } - } catch (err) /* istanbul ignore next */ { - checkForPlatformFailure(err); - logger.debug({ err }, 'Error setting git author config'); - throw new Error(TEMPORARY_ERROR); - } config.currentBranch = config.currentBranch || (await getDefaultBranch(git)); if (config.branchPrefix) { await setBranchPrefix(config.branchPrefix); @@ -710,16 +733,26 @@ export async function commitFiles({ } const { localDir } = getGlobalConfig(); await configSigningKey(localDir); + await writeGitAuthor(); try { await git.reset(ResetMode.HARD); await git.raw(['clean', '-fd']); await git.checkout(['-B', branchName, 'origin/' + config.currentBranch]); const fileNames: string[] = []; - const deleted: string[] = []; + const deletedFiles: string[] = []; + const ignoredFiles: string[] = []; for (const file of files) { // istanbul ignore if if (file.name === '|delete|') { - deleted.push(file.contents as string); + const fileName = file.contents as string; + try { + await git.rm([fileName]); + deletedFiles.push(fileName); + } catch (err) /* istanbul ignore next */ { + checkForPlatformFailure(err); + logger.warn({ err, fileName }, 'Cannot delete file'); + ignoredFiles.push(fileName); + } } else if (await isDirectory(join(localDir, file.name))) { fileNames.push(file.name); await gitAdd(file.name); @@ -735,23 +768,9 @@ export async function commitFiles({ await fs.outputFile(join(localDir, file.name), contents); } } - // istanbul ignore if - if (fileNames.length === 1 && configFileNames.includes(fileNames[0])) { - fileNames.unshift('-f'); - } if (fileNames.length) { await gitAdd(fileNames); } - if (deleted.length) { - for (const f of deleted) { - try { - await git.rm([f]); - } catch (err) /* istanbul ignore next */ { - checkForPlatformFailure(err); - logger.debug({ err }, 'Cannot delete ' + f); - } - } - } const commitOptions: Options = {}; if (getNoVerify().includes(GitNoVerifyOption.Commit)) { @@ -768,7 +787,10 @@ export async function commitFiles({ logger.warn({ commitRes }, 'Detected empty commit - aborting git push'); return null; } - logger.debug({ result: commitRes }, `git commit`); + logger.debug( + { deletedFiles, ignoredFiles, result: commitRes }, + `git commit` + ); const commit = commitRes?.commit || 'unknown'; if (!force && !(await hasDiff(`origin/${branchName}`))) { logger.debug( diff --git a/lib/util/host-rules.spec.ts b/lib/util/host-rules.spec.ts index 1e01f83c89c948..7bc63e902dde7d 100644 --- a/lib/util/host-rules.spec.ts +++ b/lib/util/host-rules.spec.ts @@ -208,6 +208,15 @@ describe('util/host-rules', () => { expect(find({ url: 'https://domain.com' }).token).toEqual('def'); expect(find({ url: 'httpsdomain.com' }).token).toBeUndefined(); }); + it('matches on matchHost with dot prefix', () => { + add({ + matchHost: '.domain.com', + token: 'def', + }); + expect(find({ url: 'https://api.domain.com' }).token).toEqual('def'); + expect(find({ url: 'https://domain.com' }).token).toBeUndefined(); + expect(find({ url: 'httpsdomain.com' }).token).toBeUndefined(); + }); it('matches on hostType and endpoint', () => { add({ hostType: datasourceNuget.id, diff --git a/lib/util/host-rules.ts b/lib/util/host-rules.ts index d9c18f536b3855..c6887c5a0ea62b 100644 --- a/lib/util/host-rules.ts +++ b/lib/util/host-rules.ts @@ -85,7 +85,10 @@ function matchesHost(rule: HostRule, search: HostRuleSearch): boolean { return false; } const { hostname } = parsedUrl; - return hostname === rule.matchHost || hostname.endsWith(`.${rule.matchHost}`); + const dotPrefixedMatchHost = rule.matchHost.startsWith('.') + ? rule.matchHost + : `.${rule.matchHost}`; + return hostname === rule.matchHost || hostname.endsWith(dotPrefixedMatchHost); } export function find(search: HostRuleSearch): HostRule { diff --git a/lib/workers/pr/changelog/release-notes.spec.ts b/lib/workers/pr/changelog/release-notes.spec.ts index 3a86c3db889fb9..622b0d618b3e37 100644 --- a/lib/workers/pr/changelog/release-notes.spec.ts +++ b/lib/workers/pr/changelog/release-notes.spec.ts @@ -68,8 +68,8 @@ describe('workers/pr/changelog/release-notes', () => { const now = DateTime.local(); it.each([ [now, 55], - [now.minus({ week: 2 }), 1435], - [now.minus({ year: 1 }), 14495], + [now.minus({ weeks: 2 }), 1435], + [now.minus({ years: 1 }), 14495], ])('works with string date (%s, %i)', (date, minutes) => { expect(releaseNotesCacheMinutes(date?.toISO())).toEqual(minutes); }); diff --git a/lib/workers/repository/onboarding/branch/index.ts b/lib/workers/repository/onboarding/branch/index.ts index 5af0f6f74b7504..370e668069f38e 100644 --- a/lib/workers/repository/onboarding/branch/index.ts +++ b/lib/workers/repository/onboarding/branch/index.ts @@ -7,7 +7,7 @@ import { } from '../../../../constants/error-messages'; import { logger } from '../../../../logger'; import { platform } from '../../../../platform'; -import { checkoutBranch } from '../../../../util/git'; +import { checkoutBranch, setGitAuthor } from '../../../../util/git'; import { extractAllDependencies } from '../../extract'; import { mergeRenovateConfig } from '../../init/merge'; import { isOnboarded, onboardingPrExists } from './check'; @@ -30,6 +30,8 @@ export async function checkOnboardingBranch( throw new Error(REPOSITORY_FORKED); } logger.debug('Repo is not onboarded'); + // global gitAuthor will need to be used + setGitAuthor(config.gitAuthor); if (await onboardingPrExists(config)) { logger.debug('Onboarding PR already exists'); const commit = await rebaseOnboardingBranch(config); diff --git a/package.json b/package.json index 4fa965180f9ec6..a899d0a30053c5 100644 --- a/package.json +++ b/package.json @@ -131,18 +131,18 @@ "@yarnpkg/parsers": "2.4.0", "azure-devops-node-api": "11.0.1", "bunyan": "1.8.15", - "cacache": "15.2.0", + "cacache": "15.3.0", "chalk": "4.1.2", "changelog-filename-regex": "2.0.1", "clean-git-ref": "2.0.1", - "commander": "7.2.0", + "commander": "8.1.0", "conventional-commits-detector": "1.0.3", "crypto-random-string": "3.3.1", "deepmerge": "4.2.2", "delay": "5.0.0", "dequal": "2.0.2", "detect-indent": "6.1.0", - "email-addresses": "4.0.0", + "email-addresses": "5.0.0", "emoji-regex": "9.2.2", "emojibase": "5.2.0", "emojibase-regex": "5.1.3", @@ -184,7 +184,7 @@ "semver-utils": "1.1.4", "shlex": "2.1.0", "shortid": "2.2.16", - "simple-git": "2.44.0", + "simple-git": "2.45.0", "slugify": "1.6.0", "traverse": "0.6.6", "upath": "2.0.1", @@ -198,9 +198,9 @@ }, "devDependencies": { "@actions/core": "1.5.0", - "@jest/globals": "27.0.6", - "@jest/reporters": "27.0.6", - "@jest/test-result": "27.0.6", + "@jest/globals": "27.1.0", + "@jest/reporters": "27.1.0", + "@jest/test-result": "27.1.0", "@ls-lint/ls-lint": "1.10.0", "@renovate/eslint-plugin": "https://github.com/renovatebot/eslint-plugin#v0.0.3", "@semantic-release/exec": "5.0.0", @@ -219,12 +219,12 @@ "@types/js-yaml": "4.0.3", "@types/json-dup-key-validator": "1.0.0", "@types/linkify-markdown": "1.0.1", - "@types/luxon": "2.0.0", - "@types/markdown-it": "12.2.0", + "@types/luxon": "2.0.1", + "@types/markdown-it": "12.2.1", "@types/markdown-table": "2.0.0", "@types/moo": "0.5.5", "@types/nock": "10.0.3", - "@types/node": "14.17.11", + "@types/node": "14.17.12", "@types/node-emoji": "1.8.1", "@types/parse-link-header": "1.0.0", "@types/registry-auth-token": "4.2.1", @@ -250,7 +250,7 @@ "glob": "7.1.7", "graphql": "15.5.1", "husky": "7.0.2", - "jest": "27.0.6", + "jest": "27.1.0", "jest-extended": "0.11.5", "jest-github-actions-reporter": "1.0.3", "jest-junit": "12.2.0", @@ -263,14 +263,14 @@ "prettier": "2.3.2", "pretty-quick": "3.1.1", "rimraf": "3.0.2", - "semantic-release": "17.4.6", + "semantic-release": "17.4.7", "shelljs": "0.8.4", "strip-ansi": "6.0.0", "tmp-promise": "3.0.2", "ts-jest": "27.0.5", "ts-node": "10.2.1", "type-fest": "2.1.0", - "typescript": "4.3.5", + "typescript": "4.4.2", "unified": "9.2.2" }, "resolutions": { diff --git a/tsconfig.json b/tsconfig.json index 920493bcb7b5e5..409aaf9567f698 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,7 @@ "noUnusedLocals": true, "noImplicitOverride": true, "experimentalDecorators": true, + "useUnknownInCatchVariables": false /* we aren't prepared for enabling this by default since ts 4.4*/, "lib": ["es2018"], "types": ["node", "jest", "jest-extended"], "allowJs": true, diff --git a/yarn.lock b/yarn.lock index b57d76e64e74c7..7b99b0a1d87b84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -983,7 +983,7 @@ chalk "^2.0.1" slash "^2.0.0" -"@jest/console@^27.0.6", "@jest/console@^27.1.0": +"@jest/console@^27.1.0": version "27.1.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.1.0.tgz#de13b603cb1d389b50c0dc6296e86e112381e43c" integrity sha512-+Vl+xmLwAXLNlqT61gmHEixeRbS4L8MUzAjtpBCOPWH+izNI/dR16IeXjkXJdRtIVWVSf9DO1gdp67B1XorZhQ== @@ -995,7 +995,7 @@ jest-util "^27.1.0" slash "^3.0.0" -"@jest/core@^27.0.6", "@jest/core@^27.1.0": +"@jest/core@^27.1.0": version "27.1.0" resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.1.0.tgz#622220f18032f5869e579cecbe744527238648bf" integrity sha512-3l9qmoknrlCFKfGdrmiQiPne+pUR4ALhKwFTYyOeKw6egfDwJkO21RJ1xf41rN8ZNFLg5W+w6+P4fUqq4EMRWA== @@ -1030,7 +1030,7 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^27.0.6", "@jest/environment@^27.1.0": +"@jest/environment@^27.1.0": version "27.1.0" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.1.0.tgz#c7224a67004759ec203d8fa44e8bc0db93f66c44" integrity sha512-wRp50aAMY2w1U2jP1G32d6FUVBNYqmk8WaGkiIEisU48qyDV0WPtw3IBLnl7orBeggveommAkuijY+RzVnNDOQ== @@ -1052,16 +1052,7 @@ jest-mock "^27.1.0" jest-util "^27.1.0" -"@jest/globals@27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.0.6.tgz#48e3903f99a4650673d8657334d13c9caf0e8f82" - integrity sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw== - dependencies: - "@jest/environment" "^27.0.6" - "@jest/types" "^27.0.6" - expect "^27.0.6" - -"@jest/globals@^27.1.0": +"@jest/globals@27.1.0", "@jest/globals@^27.1.0": version "27.1.0" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.1.0.tgz#e093a49c718dd678a782c197757775534c88d3f2" integrity sha512-73vLV4aNHAlAgjk0/QcSIzzCZSqVIPbmFROJJv9D3QUR7BI4f517gVdJpSrCHxuRH3VZFhe0yGG/tmttlMll9g== @@ -1070,37 +1061,7 @@ "@jest/types" "^27.1.0" expect "^27.1.0" -"@jest/reporters@27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.0.6.tgz#91e7f2d98c002ad5df94d5b5167c1eb0b9fd5b00" - integrity sha512-TIkBt09Cb2gptji3yJXb3EE+eVltW6BjO7frO7NEfjI9vSIYoISi5R3aI3KpEDXlB1xwB+97NXIqz84qYeYsfA== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.0.6" - "@jest/test-result" "^27.0.6" - "@jest/transform" "^27.0.6" - "@jest/types" "^27.0.6" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.2" - graceful-fs "^4.2.4" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.3" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.2" - jest-haste-map "^27.0.6" - jest-resolve "^27.0.6" - jest-util "^27.0.6" - jest-worker "^27.0.6" - slash "^3.0.0" - source-map "^0.6.0" - string-length "^4.0.1" - terminal-link "^2.0.0" - v8-to-istanbul "^8.0.0" - -"@jest/reporters@^27.1.0": +"@jest/reporters@27.1.0", "@jest/reporters@^27.1.0": version "27.1.0" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.1.0.tgz#02ed1e6601552c2f6447378533f77aad002781d4" integrity sha512-5T/zlPkN2HnK3Sboeg64L5eC8iiaZueLpttdktWTJsvALEtP2YMkC5BQxwjRWQACG9SwDmz+XjjkoxXUDMDgdw== @@ -1148,13 +1109,13 @@ graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/test-result@27.0.6": - version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.0.6.tgz#3fa42015a14e4fdede6acd042ce98c7f36627051" - integrity sha512-ja/pBOMTufjX4JLEauLxE3LQBPaI2YjGFtXexRAjt1I/MbfNlMx0sytSX3tn5hSLzQsR3Qy2rd0hc1BWojtj9w== +"@jest/test-result@27.1.0", "@jest/test-result@^27.1.0": + version "27.1.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.1.0.tgz#9345ae5f97f6a5287af9ebd54716cd84331d42e8" + integrity sha512-Aoz00gpDL528ODLghat3QSy6UBTD5EmmpjrhZZMK/v1Q2/rRRqTGnFxHuEkrD4z/Py96ZdOHxIWkkCKRpmnE1A== dependencies: - "@jest/console" "^27.0.6" - "@jest/types" "^27.0.6" + "@jest/console" "^27.1.0" + "@jest/types" "^27.1.0" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" @@ -1167,16 +1128,6 @@ "@jest/types" "^24.9.0" "@types/istanbul-lib-coverage" "^2.0.0" -"@jest/test-result@^27.0.6", "@jest/test-result@^27.1.0": - version "27.1.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.1.0.tgz#9345ae5f97f6a5287af9ebd54716cd84331d42e8" - integrity sha512-Aoz00gpDL528ODLghat3QSy6UBTD5EmmpjrhZZMK/v1Q2/rRRqTGnFxHuEkrD4z/Py96ZdOHxIWkkCKRpmnE1A== - dependencies: - "@jest/console" "^27.1.0" - "@jest/types" "^27.1.0" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - "@jest/test-sequencer@^27.1.0": version "27.1.0" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.1.0.tgz#04e8b3bd735570d3d48865e74977a14dc99bff2d" @@ -1187,7 +1138,7 @@ jest-haste-map "^27.1.0" jest-runtime "^27.1.0" -"@jest/transform@^27.0.6", "@jest/transform@^27.1.0": +"@jest/transform@^27.1.0": version "27.1.0" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.1.0.tgz#962e385517e3d1f62827fa39c305edcc3ca8544b" integrity sha512-ZRGCA2ZEVJ00ubrhkTG87kyLbN6n55g1Ilq0X9nJb5bX3MhMp3O6M7KG+LvYu+nZRqG5cXsQnJEdZbdpTAV8pQ== @@ -1228,7 +1179,7 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" -"@jest/types@^27.0.6", "@jest/types@^27.1.0": +"@jest/types@^27.1.0": version "27.1.0" resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.1.0.tgz#674a40325eab23c857ebc0689e7e191a3c5b10cc" integrity sha512-pRP5cLIzN7I7Vp6mHKRSaZD7YpBTK7hawx5si8trMKqk4+WOdK8NEKOTO2G8PKWD1HbKMVckVB6/XHh/olhf2g== @@ -1895,15 +1846,15 @@ resolved "https://registry.yarnpkg.com/@types/linkify-markdown/-/linkify-markdown-1.0.1.tgz#0b750a592107dd46ecf2b5be0eeb7656b1fc814d" integrity sha512-RYDOtCol7/sHGhSJvWVnl0AmOdQQWgUYys6cwn5Lt3RiYhyhTLMLv7B9wdixMgCfnNt0MQj/YSGi3qN0IQqLeQ== -"@types/luxon@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-2.0.0.tgz#3dd1d8c51b49e34585c5158ba3393e95c51fee89" - integrity sha512-L7iL3FitRSeuz8fbeLtql7qU6inHVtwEDWI1+vBXgyp0J2tmxOD7TgMBiEQjII/Y/TPcwrKasXb1BPuiCXRgxg== +"@types/luxon@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-2.0.1.tgz#4be7e91283de747ca061a071ef0c3747a710bda1" + integrity sha512-EqwKd+cuzQ6Jz/zsFYOofHzSfZSh1x3eBBj9+2IYk5vF3I1JnysPFK/I0YnkJ0artgvVY3jJYf2fGdIzoK0UIA== -"@types/markdown-it@12.2.0": - version "12.2.0" - resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.0.tgz#f609929ac1e50cf0d039473fb331ebc62e313b34" - integrity sha512-YEpywby5S2wt64C2E3bcpLvtIV8BuCj+4AGtL7tU51V8Vr1qwm+cX9gFfWRyclgLC0UK/7w2heYmhymDi+snzw== +"@types/markdown-it@12.2.1": + version "12.2.1" + resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.1.tgz#ca36e1edce6f15a770f3e99e68622d1d2e2f0c65" + integrity sha512-iij+ilRX/vxtUPCREjn74xzHo/RorHJDwOsJ6X+TgKw7zSvazhVXnDfwlTnyLOMdiVUjtRYU4CrcUZ7Aci4PmQ== dependencies: "@types/linkify-it" "*" "@types/mdurl" "*" @@ -1958,10 +1909,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.7.6.tgz#8666478db8095aa66e25b7e469f3e7b53ea2855e" integrity sha512-VESVNFoa/ahYA62xnLBjo5ur6gPsgEE5cNRy8SrdnkZ2nwJSW0kJ4ufbFr2zuU9ALtHM8juY53VcRoTA7htXSg== -"@types/node@14.17.11": - version "14.17.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.11.tgz#82d266d657aec5ff01ca59f2ffaff1bb43f7bf0f" - integrity sha512-n2OQ+0Bz6WEsUjrvcHD1xZ8K+Kgo4cn9/w94s1bJS690QMUWfJPW/m7CCb7gPkA1fcYwL2UpjXP/rq/Eo41m6w== +"@types/node@14.17.12": + version "14.17.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.12.tgz#7a31f720b85a617e54e42d24c4ace136601656c7" + integrity sha512-vhUqgjJR1qxwTWV5Ps5txuy2XMdf7Fw+OrdChRboy8BmWUPkckOhphaohzFG6b8DW7CrxaBMdrdJ47SYFq1okw== "@types/node@^13.7.0": version "13.13.52" @@ -2849,7 +2800,7 @@ bunyan@1.8.15: mv "~2" safe-json-stringify "~1" -cacache@*, cacache@^15.0.3, cacache@^15.0.5, cacache@^15.2.0: +cacache@*, cacache@15.3.0, cacache@^15.0.3, cacache@^15.0.5, cacache@^15.2.0: version "15.3.0" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== @@ -2873,29 +2824,6 @@ cacache@*, cacache@^15.0.3, cacache@^15.0.5, cacache@^15.2.0: tar "^6.0.2" unique-filename "^1.1.1" -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" - 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" - cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -3216,10 +3144,10 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.1.0.tgz#db36e3e66edf24ff591d639862c6ab2c52664362" + integrity sha512-mf45ldcuHSYShkplHHGKWb4TrmwQadxOn7v4WuhDJy0ZVoY5JFajaRDKD0PNe5qXzBX0rhovjTnP6Kz9LETcuA== common-ancestor-path@^1.0.1: version "1.0.1" @@ -3767,10 +3695,10 @@ electron-to-chromium@^1.3.811: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.822.tgz#7036edc7f669b0aa79e9801dc5f56866c6ddc0b2" integrity sha512-k7jG5oYYHxF4jx6PcqwHX3JVME/OjzolqOZiIogi9xtsfsmTjTdie4x88OakYFPEa8euciTgCCzvVNwvmjHb1Q== -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== +email-addresses@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/email-addresses/-/email-addresses-5.0.0.tgz#7ae9e7f58eef7d5e3e2c2c2d3ea49b78dc854fa6" + integrity sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw== emittery@^0.8.1: version "0.8.1" @@ -4208,7 +4136,7 @@ expect@^24.1.0: jest-message-util "^24.9.0" jest-regex-util "^24.9.0" -expect@^27.0.6, expect@^27.1.0: +expect@^27.1.0: version "27.1.0" resolved "https://registry.yarnpkg.com/expect/-/expect-27.1.0.tgz#380de0abb3a8f2299c4c6c66bbe930483b5dba9b" integrity sha512-9kJngV5hOJgkFil4F/uXm3hVBubUK2nERVfvqNNwxxuW8ZOUwSTTSysgfzckYtv/LBzj/LJXbiAF7okHCXgdug== @@ -5521,7 +5449,7 @@ jest-circus@^27.1.0: stack-utils "^2.0.3" throat "^6.0.1" -jest-cli@^27.0.6: +jest-cli@^27.1.0: version "27.1.0" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.1.0.tgz#118438e4d11cf6fb66cb2b2eb5778817eab3daeb" integrity sha512-h6zPUOUu+6oLDrXz0yOWY2YXvBLk8gQinx4HbZ7SF4V3HzasQf+ncoIbKENUMwXyf54/6dBkYXvXJos+gOHYZw== @@ -5660,7 +5588,7 @@ jest-github-actions-reporter@1.0.3: dependencies: "@actions/core" "^1.2.0" -jest-haste-map@^27.0.6, jest-haste-map@^27.1.0: +jest-haste-map@^27.1.0: version "27.1.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.1.0.tgz#a39f456823bd6a74e3c86ad25f6fa870428326bf" integrity sha512-7mz6LopSe+eA6cTFMf10OfLLqRoIPvmMyz5/OnSXnHO7hB0aDP1iIeLWCXzAcYU5eIJVpHr12Bk9yyq2fTW9vg== @@ -5819,7 +5747,7 @@ jest-resolve-dependencies@^27.1.0: jest-regex-util "^27.0.6" jest-snapshot "^27.1.0" -jest-resolve@^27.0.6, jest-resolve@^27.1.0: +jest-resolve@^27.1.0: version "27.1.0" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.1.0.tgz#bb22303c9e240cccdda28562e3c6fbcc6a23ac86" integrity sha512-TXvzrLyPg0vLOwcWX38ZGYeEztSEmW+cQQKqc4HKDUwun31wsBXwotRlUz4/AYU/Fq4GhbMd/ileIWZEtcdmIA== @@ -5954,7 +5882,7 @@ jest-util@^26.0.0: is-ci "^2.0.0" micromatch "^4.0.2" -jest-util@^27.0.0, jest-util@^27.0.6, jest-util@^27.1.0: +jest-util@^27.0.0, jest-util@^27.1.0: version "27.1.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.1.0.tgz#06a53777a8cb7e4940ca8e20bf9c67dd65d9bd68" integrity sha512-edSLD2OneYDKC6gZM1yc+wY/877s/fuJNoM1k3sOEpzFyeptSmke3SLnk1dDHk9CgTA+58mnfx3ew3J11Kes/w== @@ -5991,7 +5919,7 @@ jest-watcher@^27.1.0: jest-util "^27.1.0" string-length "^4.0.1" -jest-worker@^27.0.6, jest-worker@^27.1.0: +jest-worker@^27.1.0: version "27.1.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.1.0.tgz#65f4a88e37148ed984ba8ca8492d6b376938c0aa" integrity sha512-mO4PHb2QWLn9yRXGp7rkvXLAYuxwhq1ZYUo0LoDhg8wqvv4QizP1ZWEJOeolgbEgAWZLIEU0wsku8J+lGWfBhg== @@ -6000,14 +5928,14 @@ jest-worker@^27.0.6, jest-worker@^27.1.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.0.6.tgz#10517b2a628f0409087fbf473db44777d7a04505" - integrity sha512-EjV8aETrsD0wHl7CKMibKwQNQc3gIRBXlTikBmmHUeVMKaPFxdcUIBfoDqTSXDoGJIivAYGqCWVlzCSaVjPQsA== +jest@27.1.0: + version "27.1.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.1.0.tgz#eaab62dfdc02d8b7c814cd27b8d2d92bc46d3d69" + integrity sha512-pSQDVwRSwb109Ss13lcMtdfS9r8/w2Zz8+mTUA9VORD66GflCdl8nUFCqM96geOD2EBwWCNURrNAfQsLIDNBdg== dependencies: - "@jest/core" "^27.0.6" + "@jest/core" "^27.1.0" import-local "^3.0.2" - jest-cli "^27.0.6" + jest-cli "^27.1.0" js-tokens@^4.0.0: version "4.0.0" @@ -8463,10 +8391,10 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -semantic-release@17.4.6: - version "17.4.6" - resolved "https://registry.yarnpkg.com/semantic-release/-/semantic-release-17.4.6.tgz#a9e53da3d43ac07250c55b6939857587be2ecaa2" - integrity sha512-0B1OGkW8gzQjLbj3H5G4fJyy73HAWsoDnzWYyfNJHyyyLi1acQd+id4O7U+12wXTk9G83pXCgSFj9oYTvocFMA== +semantic-release@17.4.7: + version "17.4.7" + resolved "https://registry.yarnpkg.com/semantic-release/-/semantic-release-17.4.7.tgz#88e1dce7294cc43acc54c4e0a83f582264567206" + integrity sha512-3Ghu8mKCJgCG3QzE5xphkYWM19lGE3XjFdOXQIKBM2PBpBvgFQ/lXv31oX0+fuN/UjNFO/dqhNs8ATLBhg6zBg== dependencies: "@semantic-release/commit-analyzer" "^8.0.0" "@semantic-release/error" "^2.2.0" @@ -8638,10 +8566,10 @@ signale@^1.2.1: figures "^2.0.0" pkg-conf "^2.1.0" -simple-git@2.44.0: - version "2.44.0" - resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-2.44.0.tgz#94eca4162b7e21707b5a1a40b22e6f29b007d3a8" - integrity sha512-wIjcAmymhzgdaM0Y/a+XxmNGlivvHQTPZDYXVmyHMShVDwdeVqu3+OOyDbYu0DnfVzqLs2EOxRTgMNbC3YquwQ== +simple-git@2.45.0: + version "2.45.0" + resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-2.45.0.tgz#4d53b146fc23496099ebfc7af5b0d605d0e3e504" + integrity sha512-wu/Ujs9IXn0HuyYm4HyRvne+EKsjJSWKEMkB3wQa3gNHSMHt7y3oeNX9zRQ3UBPk7bRRMLLHAdIZCZfHT9ehPg== dependencies: "@kwsites/file-exists" "^1.1.1" "@kwsites/promise-deferred" "^1.1.1" @@ -9486,10 +9414,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@4.3.5: - version "4.3.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" - integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== +typescript@4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.2.tgz#6d618640d430e3569a1dfb44f7d7e600ced3ee86" + integrity sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6"