From b4909c50cf1cd84989f409bb2dd21b4baff70b9a Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Thu, 11 Mar 2021 15:19:54 +0300 Subject: [PATCH 1/6] feat(git): Specify additional git authors to ignore --- docs/usage/configuration-options.md | 14 ++++++++++++++ lib/config/definitions.ts | 8 ++++++++ lib/config/types.ts | 1 + lib/util/git/index.spec.ts | 8 ++++++-- lib/util/git/index.ts | 13 ++++++++++++- lib/workers/repository/init/index.ts | 3 ++- 6 files changed, 43 insertions(+), 4 deletions(-) diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 1addb8a6e98fc4..f730b719acd366 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -635,6 +635,20 @@ 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. +## gitIgnoredAuthors + +Specify commit authors ignored by Renovate. + +If you have other bots which commit on top of Renovate PRs, you probably want Renovate to rebase those PRs instead of treating them as modified. + +Example: + +```json +{ + "gitIgnoredAuthors": ["some-bot@example.org"] +} +``` + ## gitLabAutomerge Caution (fixed in GitLab >= 12.7): when this option is enabled it is possible due to a bug in GitLab that MRs with failing pipelines might still get merged. diff --git a/lib/config/definitions.ts b/lib/config/definitions.ts index 26deb9cc616606..eae0fb02e28e19 100644 --- a/lib/config/definitions.ts +++ b/lib/config/definitions.ts @@ -589,6 +589,14 @@ const options: RenovateOptions[] = [ admin: true, stage: 'global', }, + { + name: 'gitIgnoredAuthors', + description: + 'Additional git authors which are ignored by Renovate. Must conform to RFC5322.', + type: 'array', + subType: 'string', + stage: 'repository', + }, { name: 'enabledManagers', description: diff --git a/lib/config/types.ts b/lib/config/types.ts index 882266c56a78e5..82e2658e233082 100644 --- a/lib/config/types.ts +++ b/lib/config/types.ts @@ -61,6 +61,7 @@ export interface RenovateSharedConfig { suppressNotifications?: string[]; timezone?: string; unicodeEmoji?: boolean; + gitIgnoredAuthors?: string[]; } // Config options used only within the global worker diff --git a/lib/util/git/index.spec.ts b/lib/util/git/index.spec.ts index 1c449d709bcbf9..f6549f7561a58d 100644 --- a/lib/util/git/index.spec.ts +++ b/lib/util/git/index.spec.ts @@ -145,11 +145,15 @@ describe('platform/git', () => { it('should return false when branch is not found', async () => { expect(await git.isBranchModified('renovate/not_found')).toBe(false); }); - it('should return true when author matches', async () => { + it('should return false when author matches', async () => { expect(await git.isBranchModified('renovate/future_branch')).toBe(false); expect(await git.isBranchModified('renovate/future_branch')).toBe(false); }); - it('should return false when custom author', async () => { + it('should return false when author is ignored', async () => { + git.setIgnoredAuthors(['custom@example.com']); + expect(await git.isBranchModified('renovate/custom_author')).toBe(false); + }); + it('should return true when custom author is unknown', async () => { expect(await git.isBranchModified('renovate/custom_author')).toBe(true); }); }); diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts index dc544308303955..904fa87bd5cdee 100644 --- a/lib/util/git/index.ts +++ b/lib/util/git/index.ts @@ -1,4 +1,5 @@ import URL from 'url'; +import is from '@sindresorhus/is'; import fs from 'fs-extra'; import GitUrlParse from 'git-url-parse'; import Git, { @@ -51,6 +52,7 @@ interface LocalConfig extends StorageConfig { branchCommits: Record; branchIsModified: Record; branchPrefix: string; + ignoredAuthors: string[]; } // istanbul ignore next @@ -165,6 +167,7 @@ async function fetchBranchCommits(): Promise { export async function initRepo(args: StorageConfig): Promise { config = { ...args } as any; + config.ignoredAuthors = []; config.additionalBranches = []; config.branchIsModified = {}; git = Git(config.localDir); @@ -215,6 +218,13 @@ export async function setBranchPrefix(branchPrefix: string): Promise { } } +export function setIgnoredAuthors(ignoredAuthors?: string[]): void { + config.ignoredAuthors = []; + if (is.array(ignoredAuthors)) { + config.ignoredAuthors = ignoredAuthors; + } +} + export async function getSubmodules(): Promise { try { return ( @@ -478,7 +488,8 @@ export async function isBranchModified(branchName: string): Promise { const { gitAuthorEmail } = config; if ( lastAuthor === process.env.RENOVATE_LEGACY_GIT_AUTHOR_EMAIL || // remove in next major release - lastAuthor === gitAuthorEmail + lastAuthor === gitAuthorEmail || + config.ignoredAuthors.some((ignoredAuthor) => lastAuthor === ignoredAuthor) ) { // author matches - branch has not been modified config.branchIsModified[branchName] = false; diff --git a/lib/workers/repository/init/index.ts b/lib/workers/repository/init/index.ts index f8290e01fe70f0..15cb86d9d46323 100644 --- a/lib/workers/repository/init/index.ts +++ b/lib/workers/repository/init/index.ts @@ -1,7 +1,7 @@ import { RenovateConfig } from '../../../config'; import { logger } from '../../../logger'; import { clone } from '../../../util/clone'; -import { setBranchPrefix } from '../../../util/git'; +import { setBranchPrefix, setIgnoredAuthors } from '../../../util/git'; import { checkIfConfigured } from '../configured'; import { initApis } from './apis'; import { initializeCaches } from './cache'; @@ -21,6 +21,7 @@ export async function initRepo( config = await getRepoConfig(config); checkIfConfigured(config); await setBranchPrefix(config.branchPrefix); + setIgnoredAuthors(config.gitIgnoredAuthors); config = await detectVulnerabilityAlerts(config); // istanbul ignore if if (config.printConfig) { From f789cbf6abf862b65abd9eb6359e78fc3d9706f8 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Thu, 11 Mar 2021 18:09:10 +0300 Subject: [PATCH 2/6] Remove unnecessary type check --- lib/util/git/index.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts index 904fa87bd5cdee..33510db3a20c6c 100644 --- a/lib/util/git/index.ts +++ b/lib/util/git/index.ts @@ -1,5 +1,4 @@ import URL from 'url'; -import is from '@sindresorhus/is'; import fs from 'fs-extra'; import GitUrlParse from 'git-url-parse'; import Git, { @@ -219,10 +218,7 @@ export async function setBranchPrefix(branchPrefix: string): Promise { } export function setIgnoredAuthors(ignoredAuthors?: string[]): void { - config.ignoredAuthors = []; - if (is.array(ignoredAuthors)) { - config.ignoredAuthors = ignoredAuthors; - } + config.ignoredAuthors = ignoredAuthors ?? []; } export async function getSubmodules(): Promise { From e0154fdc1edeaade5423263d4d499ae4a7ad7f38 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Fri, 12 Mar 2021 12:44:39 +0300 Subject: [PATCH 3/6] setRepoUserConfig --- lib/util/git/index.spec.ts | 20 ++++++++++++++++---- lib/util/git/index.ts | 17 +++++++++++++++-- lib/workers/repository/init/index.ts | 5 ++--- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/lib/util/git/index.spec.ts b/lib/util/git/index.spec.ts index f6549f7561a58d..df56cb8551908c 100644 --- a/lib/util/git/index.spec.ts +++ b/lib/util/git/index.spec.ts @@ -3,6 +3,16 @@ import Git from 'simple-git'; import tmp from 'tmp-promise'; import * as git from '.'; +function userConfig( + config: Partial = {} +): git.UserRepoConfig { + return { + branchPrefix: 'renovate/', + gitIgnoredAuthors: [], + ...config, + }; +} + describe('platform/git', () => { jest.setTimeout(15000); @@ -73,7 +83,7 @@ describe('platform/git', () => { gitAuthorName: 'Jest', gitAuthorEmail: 'Jest@example.com', }); - await git.setBranchPrefix('renovate/'); + await git.setUserRepoConfig(userConfig()); await git.syncGit(); }); @@ -150,7 +160,9 @@ describe('platform/git', () => { expect(await git.isBranchModified('renovate/future_branch')).toBe(false); }); it('should return false when author is ignored', async () => { - git.setIgnoredAuthors(['custom@example.com']); + await git.setUserRepoConfig( + userConfig({ gitIgnoredAuthors: ['custom@example.com'] }) + ); expect(await git.isBranchModified('renovate/custom_author')).toBe(false); }); it('should return true when custom author is unknown', async () => { @@ -393,7 +405,7 @@ describe('platform/git', () => { url: base.path, }); - await git.setBranchPrefix('renovate/'); + await git.setUserRepoConfig(userConfig({ branchPrefix: 'renovate/' })); expect(git.branchExists('renovate/test')).toBe(true); await git.initRepo({ @@ -404,7 +416,7 @@ describe('platform/git', () => { await repo.checkout('renovate/test'); await repo.commit('past message3', ['--amend']); - await git.setBranchPrefix('renovate/'); + await git.setUserRepoConfig(userConfig({ branchPrefix: 'renovate/' })); expect(git.branchExists('renovate/test')).toBe(true); }); diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts index 33510db3a20c6c..a1bd1b3d211fb4 100644 --- a/lib/util/git/index.ts +++ b/lib/util/git/index.ts @@ -202,7 +202,7 @@ async function cleanLocalBranches(): Promise { * When we initially clone, we clone only the default branch so how no knowledge of other branches existing. * By calling this function once the repo's branchPrefix is known, we can fetch all of Renovate's branches in one command. */ -export async function setBranchPrefix(branchPrefix: string): Promise { +async function setBranchPrefix(branchPrefix: string): Promise { config.branchPrefix = branchPrefix; // If the repo is already cloned then set branchPrefix now, otherwise it will be called again during syncGit() if (gitInitialized) { @@ -217,10 +217,23 @@ export async function setBranchPrefix(branchPrefix: string): Promise { } } -export function setIgnoredAuthors(ignoredAuthors?: string[]): void { +function setIgnoredAuthors(ignoredAuthors?: string[]): void { config.ignoredAuthors = ignoredAuthors ?? []; } +export interface UserRepoConfig { + branchPrefix: string; + gitIgnoredAuthors: string[]; +} + +export async function setUserRepoConfig({ + branchPrefix, + gitIgnoredAuthors, +}: Partial): Promise { + await setBranchPrefix(branchPrefix); + setIgnoredAuthors(gitIgnoredAuthors); +} + export async function getSubmodules(): Promise { try { return ( diff --git a/lib/workers/repository/init/index.ts b/lib/workers/repository/init/index.ts index 15cb86d9d46323..da3a12015119fe 100644 --- a/lib/workers/repository/init/index.ts +++ b/lib/workers/repository/init/index.ts @@ -1,7 +1,7 @@ import { RenovateConfig } from '../../../config'; import { logger } from '../../../logger'; import { clone } from '../../../util/clone'; -import { setBranchPrefix, setIgnoredAuthors } from '../../../util/git'; +import { setUserRepoConfig } from '../../../util/git'; import { checkIfConfigured } from '../configured'; import { initApis } from './apis'; import { initializeCaches } from './cache'; @@ -20,8 +20,7 @@ export async function initRepo( config = await initApis(config); config = await getRepoConfig(config); checkIfConfigured(config); - await setBranchPrefix(config.branchPrefix); - setIgnoredAuthors(config.gitIgnoredAuthors); + await setUserRepoConfig(config); config = await detectVulnerabilityAlerts(config); // istanbul ignore if if (config.printConfig) { From 77a7527c5f6dd1964db55894dd498c5078ab8e44 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Sat, 13 Mar 2021 11:29:36 +0300 Subject: [PATCH 4/6] Simplify code --- lib/util/git/index.ts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts index ad711f94c1bbaf..e06e53fe9105c4 100644 --- a/lib/util/git/index.ts +++ b/lib/util/git/index.ts @@ -9,6 +9,7 @@ import Git, { } from 'simple-git'; import { join } from 'upath'; import { configFileNames } from '../../config/app-strings'; +import { RenovateConfig } from '../../config/types'; import { CONFIG_VALIDATION, REPOSITORY_DISABLED, @@ -217,21 +218,12 @@ async function setBranchPrefix(branchPrefix: string): Promise { } } -function setIgnoredAuthors(ignoredAuthors?: string[]): void { - config.ignoredAuthors = ignoredAuthors ?? []; -} - -export interface UserRepoConfig { - branchPrefix: string; - gitIgnoredAuthors: string[]; -} - export async function setUserRepoConfig({ branchPrefix, gitIgnoredAuthors, -}: Partial): Promise { +}: RenovateConfig): Promise { await setBranchPrefix(branchPrefix); - setIgnoredAuthors(gitIgnoredAuthors); + config.ignoredAuthors = gitIgnoredAuthors ?? []; } export async function getSubmodules(): Promise { From a7926fe1a5687a468f77d31e0ae2ba29067e7091 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Sat, 13 Mar 2021 11:52:21 +0300 Subject: [PATCH 5/6] Fix --- lib/util/git/index.spec.ts | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/lib/util/git/index.spec.ts b/lib/util/git/index.spec.ts index df56cb8551908c..ec59af45b971a0 100644 --- a/lib/util/git/index.spec.ts +++ b/lib/util/git/index.spec.ts @@ -3,16 +3,6 @@ import Git from 'simple-git'; import tmp from 'tmp-promise'; import * as git from '.'; -function userConfig( - config: Partial = {} -): git.UserRepoConfig { - return { - branchPrefix: 'renovate/', - gitIgnoredAuthors: [], - ...config, - }; -} - describe('platform/git', () => { jest.setTimeout(15000); @@ -83,7 +73,7 @@ describe('platform/git', () => { gitAuthorName: 'Jest', gitAuthorEmail: 'Jest@example.com', }); - await git.setUserRepoConfig(userConfig()); + await git.setUserRepoConfig({ branchPrefix: 'renovate/' }); await git.syncGit(); }); @@ -160,9 +150,9 @@ describe('platform/git', () => { expect(await git.isBranchModified('renovate/future_branch')).toBe(false); }); it('should return false when author is ignored', async () => { - await git.setUserRepoConfig( - userConfig({ gitIgnoredAuthors: ['custom@example.com'] }) - ); + await git.setUserRepoConfig({ + gitIgnoredAuthors: ['custom@example.com'], + }); expect(await git.isBranchModified('renovate/custom_author')).toBe(false); }); it('should return true when custom author is unknown', async () => { @@ -405,7 +395,7 @@ describe('platform/git', () => { url: base.path, }); - await git.setUserRepoConfig(userConfig({ branchPrefix: 'renovate/' })); + await git.setUserRepoConfig({ branchPrefix: 'renovate/' }); expect(git.branchExists('renovate/test')).toBe(true); await git.initRepo({ @@ -416,7 +406,7 @@ describe('platform/git', () => { await repo.checkout('renovate/test'); await repo.commit('past message3', ['--amend']); - await git.setUserRepoConfig(userConfig({ branchPrefix: 'renovate/' })); + await git.setUserRepoConfig({ branchPrefix: 'renovate/' }); expect(git.branchExists('renovate/test')).toBe(true); }); From aeb88e143bc63eaadc04df6511df29c7de5d4963 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Sat, 13 Mar 2021 18:47:26 +0400 Subject: [PATCH 6/6] Update docs/usage/configuration-options.md Co-authored-by: Rhys Arkins --- docs/usage/configuration-options.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index f730b719acd366..9a36bc8b09a1ad 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -639,7 +639,9 @@ For example, if you are following a tag like `next` and then that stream is rele Specify commit authors ignored by Renovate. -If you have other bots which commit on top of Renovate PRs, you probably want Renovate to rebase those PRs instead of treating them as modified. +By default, Renovate will treat any PR as modified if another git author has added to the branch. +When a PR is considered modified, Renovate won't perform any further commits such as if it's conflicted or needs a version update. +If you have other bots which commit on top of Renovate PRs, and don't want Renovate to treat these PRs as modified, then add the other git author(s) to `gitIgnoredAuthors`. Example: