From ea0bcdd18575cc0f9a5639adef874b3104063646 Mon Sep 17 00:00:00 2001 From: renovate-testing <77205328+renovate-testing@users.noreply.github.com> Date: Sun, 14 Mar 2021 11:27:40 +0300 Subject: [PATCH] feat(git): Specify additional git authors to ignore (#9082) --- docs/usage/configuration-options.md | 16 ++++++++++++++++ lib/config/definitions.ts | 8 ++++++++ lib/config/types.ts | 1 + lib/util/git/index.spec.ts | 16 +++++++++++----- lib/util/git/index.ts | 16 ++++++++++++++-- lib/workers/repository/init/index.ts | 4 ++-- 6 files changed, 52 insertions(+), 9 deletions(-) diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 1addb8a6e98fc4..9a36bc8b09a1ad 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -635,6 +635,22 @@ 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. + +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: + +```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..ec59af45b971a0 100644 --- a/lib/util/git/index.spec.ts +++ b/lib/util/git/index.spec.ts @@ -73,7 +73,7 @@ describe('platform/git', () => { gitAuthorName: 'Jest', gitAuthorEmail: 'Jest@example.com', }); - await git.setBranchPrefix('renovate/'); + await git.setUserRepoConfig({ branchPrefix: 'renovate/' }); await git.syncGit(); }); @@ -145,11 +145,17 @@ 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 () => { + 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 () => { expect(await git.isBranchModified('renovate/custom_author')).toBe(true); }); }); @@ -389,7 +395,7 @@ describe('platform/git', () => { url: base.path, }); - await git.setBranchPrefix('renovate/'); + await git.setUserRepoConfig({ branchPrefix: 'renovate/' }); expect(git.branchExists('renovate/test')).toBe(true); await git.initRepo({ @@ -400,7 +406,7 @@ describe('platform/git', () => { await repo.checkout('renovate/test'); await repo.commit('past message3', ['--amend']); - await git.setBranchPrefix('renovate/'); + await git.setUserRepoConfig({ branchPrefix: 'renovate/' }); expect(git.branchExists('renovate/test')).toBe(true); }); diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts index b0504719557203..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, @@ -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); @@ -200,7 +203,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) { @@ -215,6 +218,14 @@ export async function setBranchPrefix(branchPrefix: string): Promise { } } +export async function setUserRepoConfig({ + branchPrefix, + gitIgnoredAuthors, +}: RenovateConfig): Promise { + await setBranchPrefix(branchPrefix); + config.ignoredAuthors = gitIgnoredAuthors ?? []; +} + export async function getSubmodules(): Promise { try { return ( @@ -478,7 +489,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..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 } from '../../../util/git'; +import { setUserRepoConfig } from '../../../util/git'; import { checkIfConfigured } from '../configured'; import { initApis } from './apis'; import { initializeCaches } from './cache'; @@ -20,7 +20,7 @@ export async function initRepo( config = await initApis(config); config = await getRepoConfig(config); checkIfConfigured(config); - await setBranchPrefix(config.branchPrefix); + await setUserRepoConfig(config); config = await detectVulnerabilityAlerts(config); // istanbul ignore if if (config.printConfig) {