diff --git a/lib/platform/__snapshots__/index.spec.ts.snap b/lib/platform/__snapshots__/index.spec.ts.snap index b7eb3bbe15110c..b7d428596ea8be 100644 --- a/lib/platform/__snapshots__/index.spec.ts.snap +++ b/lib/platform/__snapshots__/index.spec.ts.snap @@ -1,5 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`platform escapes names 1`] = `"name [what]"`; + exports[`platform has a list of supported methods for azure 1`] = ` Array [ "addAssignees", @@ -210,3 +212,17 @@ Object { "platform": "bitbucket", } `; + +exports[`platform parses bot email 1`] = ` +Object { + "address": "some[bot]@users.noreply.github.com", + "name": "some[bot]", +} +`; + +exports[`platform parses bot name and email 1`] = ` +Object { + "address": "some[bot]@users.noreply.github.com", + "name": "some[bot]", +} +`; diff --git a/lib/platform/index.spec.ts b/lib/platform/index.spec.ts index 5a1cd17fc14566..ab87556b34a7db 100644 --- a/lib/platform/index.spec.ts +++ b/lib/platform/index.spec.ts @@ -116,4 +116,28 @@ describe('platform', () => { const bitbucketMethods = Object.keys(bitbucketServer).sort(); expect(bitbucketMethods).toMatchObject(githubMethods); }); + + it('returns null if empty email given', () => { + expect(platform.parseGitAuthor(undefined)).toBeNull(); + }); + it('parses bot email', () => { + expect( + platform.parseGitAuthor('some[bot]@users.noreply.github.com') + ).toMatchSnapshot(); + }); + it('parses bot name and email', () => { + expect( + platform.parseGitAuthor( + '"some[bot]" ' + ) + ).toMatchSnapshot(); + }); + it('escapes names', () => { + 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 531be8c536bc1e..3e8eb6e41284f0 100644 --- a/lib/platform/index.ts +++ b/lib/platform/index.ts @@ -37,6 +37,48 @@ 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: RenovateConfig ): Promise { @@ -56,12 +98,7 @@ export async function initPlatform( logger.debug('Using platform gitAuthor: ' + platformInfo.gitAuthor); gitAuthor = platformInfo.gitAuthor; } - let gitAuthorParsed: addrs.ParsedMailbox | null = null; - try { - gitAuthorParsed = addrs.parseOneAddress(gitAuthor) as addrs.ParsedMailbox; - } catch (err) /* istanbul ignore next */ { - logger.debug({ gitAuthor, err }, 'Error parsing gitAuthor'); - } + const gitAuthorParsed = parseGitAuthor(gitAuthor); // istanbul ignore if if (!gitAuthorParsed) { throw new Error('Init: gitAuthor is not parsed as valid RFC5322 format');