feat: branch status unification (#5658)

This commit is contained in:
Rhys Arkins 2020-03-08 15:03:19 +01:00 committed by GitHub
parent 51a89a3107
commit 588616f669
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 358 additions and 382 deletions

View file

@ -1,8 +0,0 @@
// Branch Status
export const BRANCH_STATUS_SUCCESS = 'success';
export const BRANCH_STATUS_FAILURE = 'failure';
export const BRANCH_STATUS_ERROR = 'error';
export const BRANCH_STATUS_PENDING = 'pending';
export const BRANCH_STATUS_CREATED = 'created';
export const BRANCH_STATUS_RUNNING = 'running';
export const BRANCH_STATUS_FAILED = 'failed';

View file

@ -2,11 +2,7 @@ import is from '@sindresorhus/is';
import * as _hostRules from '../../util/host-rules';
import { RepoParams, Platform } from '../common';
import { REPOSITORY_DISABLED } from '../../constants/error-messages';
import {
BRANCH_STATUS_FAILED,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
describe('platform/azure', () => {
let hostRules: jest.Mocked<typeof _hostRules>;
@ -416,12 +412,12 @@ describe('platform/azure', () => {
it('return success if requiredStatusChecks null', async () => {
await initRepo('some-repo');
const res = await azure.getBranchStatus('somebranch', null);
expect(res).toEqual(BRANCH_STATUS_SUCCESS);
expect(res).toEqual(BranchStatus.green);
});
it('return failed if unsupported requiredStatusChecks', async () => {
await initRepo('some-repo');
const res = await azure.getBranchStatus('somebranch', ['foo']);
expect(res).toEqual(BRANCH_STATUS_FAILED);
expect(res).toEqual(BranchStatus.red);
});
it('should pass through success', async () => {
await initRepo({ repository: 'some/repo' });
@ -432,7 +428,7 @@ describe('platform/azure', () => {
} as any)
);
const res = await azure.getBranchStatus('somebranch', []);
expect(res).toEqual(BRANCH_STATUS_SUCCESS);
expect(res).toEqual(BranchStatus.green);
});
it('should pass through failed', async () => {
await initRepo({ repository: 'some/repo' });
@ -443,7 +439,7 @@ describe('platform/azure', () => {
} as any)
);
const res = await azure.getBranchStatus('somebranch', []);
expect(res).toEqual(BRANCH_STATUS_PENDING);
expect(res).toEqual(BranchStatus.yellow);
});
});
@ -856,7 +852,7 @@ describe('platform/azure', () => {
branchName: 'test',
context: 'test',
description: 'test',
state: 'test',
state: BranchStatus.yellow,
url: 'test',
});
expect(res).toBeUndefined();

View file

@ -20,7 +20,6 @@ import {
FindPRConfig,
EnsureCommentConfig,
EnsureIssueResult,
BranchStatus,
} from '../common';
import { sanitize } from '../../util/sanitize';
import { smartTruncate } from '../utils/pr-body';
@ -31,11 +30,7 @@ import {
PR_STATE_NOT_OPEN,
PR_STATE_OPEN,
} from '../../constants/pull-requests';
import {
BRANCH_STATUS_FAILED,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
import { RenovateConfig } from '../../config';
interface Config {
@ -393,7 +388,7 @@ export /* istanbul ignore next */ function getCommitMessages(): Promise<
export async function getBranchStatusCheck(
branchName: string,
context?: string
context: string
): Promise<BranchStatus> {
logger.trace(`getBranchStatusCheck(${branchName}, ${context})`);
const azureApiGit = await azureApi.gitApi();
@ -402,9 +397,9 @@ export async function getBranchStatusCheck(
azureHelper.getBranchNameWithoutRefsheadsPrefix(branchName)!
);
if (branch.aheadCount === 0) {
return BRANCH_STATUS_SUCCESS;
return BranchStatus.green;
}
return BRANCH_STATUS_PENDING;
return BranchStatus.yellow;
}
export async function getBranchStatus(
@ -414,14 +409,14 @@ export async function getBranchStatus(
logger.debug(`getBranchStatus(${branchName})`);
if (!requiredStatusChecks) {
// null means disable status checks, so it always succeeds
return BRANCH_STATUS_SUCCESS;
return BranchStatus.green;
}
if (requiredStatusChecks.length) {
// This is Unsupported
logger.warn({ requiredStatusChecks }, `Unsupported requiredStatusChecks`);
return BRANCH_STATUS_FAILED;
return BranchStatus.red;
}
const branchStatusCheck = await getBranchStatusCheck(branchName);
const branchStatusCheck = await getBranchStatusCheck(branchName, null);
return branchStatusCheck;
}

View file

@ -7,12 +7,7 @@ import {
REPOSITORY_NOT_FOUND,
} from '../../constants/error-messages';
import { PR_STATE_CLOSED, PR_STATE_OPEN } from '../../constants/pull-requests';
import {
BRANCH_STATUS_FAILED,
BRANCH_STATUS_FAILURE,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
describe('platform/bitbucket-server', () => {
Object.entries(responses).forEach(([scenarioName, mockResponses]) => {
@ -787,11 +782,11 @@ Followed by some information.
await expect(
bitbucket.getBranchStatus('somebranch', [])
).resolves.toEqual(BRANCH_STATUS_SUCCESS);
).resolves.toEqual(BranchStatus.green);
await expect(
bitbucket.getBranchStatus('somebranch')
).resolves.toEqual(BRANCH_STATUS_SUCCESS);
).resolves.toEqual(BranchStatus.green);
expect(api.get.mock.calls).toMatchSnapshot();
});
@ -809,7 +804,7 @@ Followed by some information.
await expect(
bitbucket.getBranchStatus('somebranch', [])
).resolves.toEqual(BRANCH_STATUS_PENDING);
).resolves.toEqual(BranchStatus.yellow);
api.get.mockReturnValueOnce({
body: {
@ -821,7 +816,7 @@ Followed by some information.
await expect(
bitbucket.getBranchStatus('somebranch', [])
).resolves.toEqual(BRANCH_STATUS_PENDING);
).resolves.toEqual(BranchStatus.yellow);
expect(api.get.mock.calls).toMatchSnapshot();
});
@ -840,7 +835,7 @@ Followed by some information.
await expect(
bitbucket.getBranchStatus('somebranch', [])
).resolves.toEqual(BRANCH_STATUS_FAILED);
).resolves.toEqual(BranchStatus.red);
api.get.mockImplementationOnce(() => {
throw new Error('requst-failed');
@ -848,7 +843,7 @@ Followed by some information.
await expect(
bitbucket.getBranchStatus('somebranch', [])
).resolves.toEqual(BRANCH_STATUS_FAILED);
).resolves.toEqual(BranchStatus.red);
expect(api.get.mock.calls).toMatchSnapshot();
});
@ -889,7 +884,7 @@ Followed by some information.
await expect(
bitbucket.getBranchStatusCheck('somebranch', 'context-2')
).resolves.toEqual(BRANCH_STATUS_SUCCESS);
).resolves.toEqual(BranchStatus.green);
expect(api.get.mock.calls).toMatchSnapshot();
});
@ -912,7 +907,7 @@ Followed by some information.
await expect(
bitbucket.getBranchStatusCheck('somebranch', 'context-2')
).resolves.toEqual(BRANCH_STATUS_PENDING);
).resolves.toEqual(BranchStatus.yellow);
expect(api.get.mock.calls).toMatchSnapshot();
});
@ -935,7 +930,7 @@ Followed by some information.
await expect(
bitbucket.getBranchStatusCheck('somebranch', 'context-2')
).resolves.toEqual(BRANCH_STATUS_FAILURE);
).resolves.toEqual(BranchStatus.red);
expect(api.get.mock.calls).toMatchSnapshot();
});
@ -976,28 +971,28 @@ Followed by some information.
branchName: 'somebranch',
context: 'context-2',
description: null as any,
state: BRANCH_STATUS_SUCCESS,
state: BranchStatus.green,
});
await bitbucket.setBranchStatus({
branchName: 'somebranch',
context: 'context-2',
description: null as any,
state: BRANCH_STATUS_FAILED,
state: BranchStatus.red,
});
await bitbucket.setBranchStatus({
branchName: 'somebranch',
context: 'context-2',
description: null as any,
state: BRANCH_STATUS_FAILURE,
state: BranchStatus.red,
});
await bitbucket.setBranchStatus({
branchName: 'somebranch',
context: 'context-2',
description: null as any,
state: BRANCH_STATUS_PENDING,
state: BranchStatus.yellow,
});
api.post.mockImplementationOnce(() => {
@ -1008,14 +1003,14 @@ Followed by some information.
branchName: 'somebranch',
context: 'context-2',
description: null as any,
state: BRANCH_STATUS_SUCCESS,
state: BranchStatus.green,
});
await bitbucket.setBranchStatus({
branchName: 'somebranch',
context: 'context-1',
description: null as any,
state: BRANCH_STATUS_SUCCESS,
state: BranchStatus.green,
});
expect(api.get.mock.calls).toMatchSnapshot();

View file

@ -20,7 +20,6 @@ import {
EnsureCommentConfig,
EnsureIssueResult,
EnsureIssueConfig,
BranchStatus,
} from '../common';
import { sanitize } from '../../util/sanitize';
import { smartTruncate } from '../utils/pr-body';
@ -31,12 +30,7 @@ import {
REPOSITORY_NOT_FOUND,
} from '../../constants/error-messages';
import { PR_STATE_ALL, PR_STATE_OPEN } from '../../constants/pull-requests';
import {
BRANCH_STATUS_FAILED,
BRANCH_STATUS_FAILURE,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
import { RenovateConfig } from '../../config';
/*
* Version: 5.3 (EOL Date: 15 Aug 2019)
@ -536,7 +530,7 @@ export async function getBranchStatus(
if (!requiredStatusChecks) {
// null means disable status checks, so it always succeeds
logger.debug('Status checks disabled = returning "success"');
return BRANCH_STATUS_SUCCESS;
return BranchStatus.green;
}
if (!(await branchExists(branchName))) {
@ -548,14 +542,14 @@ export async function getBranchStatus(
logger.debug({ commitStatus }, 'branch status check result');
if (commitStatus.failed > 0) return BRANCH_STATUS_FAILED;
if (commitStatus.inProgress > 0) return BRANCH_STATUS_PENDING;
if (commitStatus.failed > 0) return BranchStatus.red;
if (commitStatus.inProgress > 0) return BranchStatus.yellow;
return commitStatus.successful > 0
? BRANCH_STATUS_SUCCESS
: BRANCH_STATUS_PENDING;
? BranchStatus.green
: BranchStatus.yellow;
} catch (err) {
logger.warn({ err }, `Failed to get branch status`);
return BRANCH_STATUS_FAILED;
return BranchStatus.red;
}
}
@ -576,7 +570,7 @@ async function getStatusCheck(
export async function getBranchStatusCheck(
branchName: string,
context: string
): Promise<string | null> {
): Promise<BranchStatus | null> {
logger.debug(`getBranchStatusCheck(${branchName}, context=${context})`);
try {
@ -586,12 +580,12 @@ export async function getBranchStatusCheck(
if (state.key === context) {
switch (state.state) {
case 'SUCCESSFUL':
return BRANCH_STATUS_SUCCESS;
return BranchStatus.green;
case 'INPROGRESS':
return BRANCH_STATUS_PENDING;
return BranchStatus.yellow;
case 'FAILED':
default:
return BRANCH_STATUS_FAILURE;
return BranchStatus.red;
}
}
}
@ -626,13 +620,13 @@ export async function setBranchStatus({
};
switch (state) {
case BRANCH_STATUS_SUCCESS:
case BranchStatus.green:
body.state = 'SUCCESSFUL';
break;
case BRANCH_STATUS_PENDING:
case BranchStatus.yellow:
body.state = 'INPROGRESS';
break;
case BRANCH_STATUS_FAILURE:
case BranchStatus.red:
default:
body.state = 'FAILED';
break;

View file

@ -2,11 +2,7 @@ import URL from 'url';
import responses from './__fixtures__/responses';
import { GotApi, RepoParams, Platform } from '../common';
import { REPOSITORY_DISABLED } from '../../constants/error-messages';
import {
BRANCH_STATUS_FAILED,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
describe('platform/bitbucket', () => {
let bitbucket: Platform;
@ -197,17 +193,15 @@ describe('platform/bitbucket', () => {
const getBranchStatus = wrap('getBranchStatus');
it('works', async () => {
await initRepo();
expect(await getBranchStatus('master', null)).toBe(BRANCH_STATUS_SUCCESS);
expect(await getBranchStatus('master', ['foo'])).toBe(
BRANCH_STATUS_FAILED
);
expect(await getBranchStatus('master', true)).toBe(BRANCH_STATUS_FAILED);
expect(await getBranchStatus('branch', true)).toBe(BRANCH_STATUS_SUCCESS);
expect(await getBranchStatus('master', null)).toBe(BranchStatus.green);
expect(await getBranchStatus('master', ['foo'])).toBe(BranchStatus.red);
expect(await getBranchStatus('master', true)).toBe(BranchStatus.red);
expect(await getBranchStatus('branch', true)).toBe(BranchStatus.green);
expect(await getBranchStatus('pending/branch', true)).toBe(
BRANCH_STATUS_PENDING
BranchStatus.yellow
);
expect(await getBranchStatus('branch-with-empty-status', true)).toBe(
BRANCH_STATUS_PENDING
BranchStatus.yellow
);
});
});
@ -217,7 +211,9 @@ describe('platform/bitbucket', () => {
it('works', async () => {
await initRepo();
expect(await getBranchStatusCheck('master', null)).toBeNull();
expect(await getBranchStatusCheck('master', 'foo')).toBe('failed');
expect(await getBranchStatusCheck('master', 'foo')).toBe(
BranchStatus.red
);
expect(await getBranchStatusCheck('master', 'bar')).toBeNull();
});
});
@ -230,7 +226,7 @@ describe('platform/bitbucket', () => {
branchName: 'branch',
context: 'context',
description: 'description',
state: BRANCH_STATUS_FAILED,
state: BranchStatus.red,
url: 'targetUrl',
});
expect(api.post.mock.calls).toMatchSnapshot();

View file

@ -20,7 +20,6 @@ import {
FindPRConfig,
EnsureCommentConfig,
EnsureIssueResult,
BranchStatus,
} from '../common';
import { sanitize } from '../../util/sanitize';
import { smartTruncate } from '../utils/pr-body';
@ -30,11 +29,7 @@ import {
} from '../../constants/error-messages';
import { PR_STATE_ALL, PR_STATE_OPEN } from '../../constants/pull-requests';
import { PLATFORM_TYPE_BITBUCKET } from '../../constants/platforms';
import {
BRANCH_STATUS_FAILED,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
import { RenovateConfig } from '../../config';
let config: utils.Config = {} as any;
@ -415,46 +410,47 @@ export async function getBranchStatus(
if (!requiredStatusChecks) {
// null means disable status checks, so it always succeeds
logger.debug('Status checks disabled = returning "success"');
return BRANCH_STATUS_SUCCESS;
return BranchStatus.green;
}
if (requiredStatusChecks.length) {
// This is Unsupported
logger.warn({ requiredStatusChecks }, `Unsupported requiredStatusChecks`);
return BRANCH_STATUS_FAILED;
return BranchStatus.red;
}
const statuses = await getStatus(branchName);
logger.debug({ branch: branchName, statuses }, 'branch status check result');
if (!statuses.length) {
logger.debug('empty branch status check result = returning "pending"');
return BRANCH_STATUS_PENDING;
return BranchStatus.yellow;
}
const noOfFailures = statuses.filter(
(status: { state: string }) => status.state === 'FAILED'
).length;
if (noOfFailures) {
return BRANCH_STATUS_FAILED;
return BranchStatus.red;
}
const noOfPending = statuses.filter(
(status: { state: string }) => status.state === 'INPROGRESS'
).length;
if (noOfPending) {
return BRANCH_STATUS_PENDING;
return BranchStatus.yellow;
}
return BRANCH_STATUS_SUCCESS;
return BranchStatus.green;
}
const bbToRenovateStatusMapping: Record<string, BranchStatus> = {
SUCCESSFUL: BranchStatus.green,
INPROGRESS: BranchStatus.yellow,
FAILED: BranchStatus.red,
};
export async function getBranchStatusCheck(
branchName: string,
context: string
): Promise<string | null> {
): Promise<BranchStatus | null> {
const statuses = await getStatus(branchName);
const bbState = (statuses.find(status => status.key === context) || {}).state;
return (
Object.keys(utils.buildStates).find(
stateKey => utils.buildStates[stateKey] === bbState
) || null
);
return bbToRenovateStatusMapping[bbState] || null;
}
export async function setBranchStatus({

View file

@ -3,6 +3,7 @@ import { api } from './bb-got-wrapper';
import { Storage } from '../git/storage';
import { GotResponse, Pr } from '../common';
import { PR_STATE_CLOSED } from '../../constants/pull-requests';
import { BranchStatus } from '../../types';
export interface Config {
baseBranch: string;
@ -59,15 +60,10 @@ export const prStates = {
all: ['OPEN', 'MERGED', 'DECLINED', 'SUPERSEDED'],
};
export const buildStates: {
[key: string]: BitbucketBranchState;
success: BitbucketBranchState;
failed: BitbucketBranchState;
pending: BitbucketBranchState;
} = {
success: 'SUCCESSFUL',
failed: 'FAILED',
pending: 'INPROGRESS',
export const buildStates: Record<BranchStatus, BitbucketBranchState> = {
green: 'SUCCESSFUL',
red: 'FAILED',
yellow: 'INPROGRESS',
};
const addMaxLength = (inputUrl: string, pagelen = 100): string => {

View file

@ -2,6 +2,7 @@ import got from 'got';
import Git from 'simple-git/promise';
import { RenovateConfig } from '../config/common';
import { CommitFilesConfig } from './git/storage';
import { BranchStatus } from '../types';
export interface FileData {
name: string;
@ -95,14 +96,6 @@ export interface Issue {
state?: string;
title?: string;
}
export type BranchStatus =
| 'pending'
| 'success'
| 'failed'
| 'running'
| 'failure';
export type PlatformPrOptions = {
azureAutoComplete?: boolean;
statusCheckVerify?: boolean;
@ -126,7 +119,7 @@ export interface BranchStatusConfig {
branchName: string;
context: string;
description: string;
state: string | null;
state: BranchStatus;
url?: string;
}
export interface FindPRConfig {
@ -175,7 +168,10 @@ export interface Platform {
getRepoForceRebase(): Promise<boolean>;
deleteLabel(number: number, label: string): Promise<void>;
setBranchStatus(branchStatusConfig: BranchStatusConfig): Promise<void>;
getBranchStatusCheck(branchName: string, context: string): Promise<string>;
getBranchStatusCheck(
branchName: string,
context: string
): Promise<BranchStatus | null>;
ensureCommentRemoval(number: number, subject: string): Promise<void>;
deleteBranch(branchName: string, closePr?: boolean): Promise<void>;
ensureComment(ensureComment: EnsureCommentConfig): Promise<boolean>;

View file

@ -2,6 +2,7 @@ import { URLSearchParams } from 'url';
import { api, GiteaGotOptions } from './gitea-got-wrapper';
import { GotResponse } from '../common';
import { PR_STATE_CLOSED } from '../../constants/pull-requests';
import { BranchStatus } from '../../types';
export type PRState = 'open' | 'closed' | 'all';
export type IssueState = 'open' | 'closed' | 'all';
@ -474,6 +475,27 @@ export async function createCommitStatus(
return res.body;
}
export const giteaToRenovateStatusMapping: Record<
CommitStatusType,
BranchStatus | null
> = {
unknown: BranchStatus.yellow,
success: BranchStatus.green,
pending: BranchStatus.yellow,
warning: BranchStatus.red,
failure: BranchStatus.red,
error: BranchStatus.red,
};
export const renovateToGiteaStatusMapping: Record<
BranchStatus,
CommitStatusType
> = {
green: 'success',
yellow: 'pending',
red: 'failure',
};
export async function getCombinedCommitStatus(
repoPath: string,
branchName: string,

View file

@ -10,7 +10,6 @@ import {
REPOSITORY_MIRRORED,
} from '../../constants/error-messages';
import {
BranchStatus,
BranchStatusConfig,
GotResponse,
RepoConfig,
@ -18,11 +17,7 @@ import {
Platform,
} from '..';
import { logger as _logger } from '../../logger';
import {
BRANCH_STATUS_FAILED,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
import { GiteaGotApi } from './gitea-got-wrapper';
import { CommitFilesConfig, File } from '../git/storage';
@ -371,7 +366,7 @@ describe('platform/gitea', () => {
await initFakeRepo();
await gitea.setBranchStatus({
branchName: 'some-branch',
state: 'some-state',
state: BranchStatus.green,
context: 'some-context',
description: 'some-description',
...bsc,
@ -386,7 +381,7 @@ describe('platform/gitea', () => {
mockRepo.full_name,
mockCommitHash,
{
state: 'some-state',
state: 'success',
context: 'some-context',
description: 'some-description',
}
@ -416,7 +411,7 @@ describe('platform/gitea', () => {
mockRepo.full_name,
mockCommitHash,
{
state: 'some-state',
state: 'success',
context: 'some-context',
description: 'some-description',
target_url: 'some-url',
@ -446,30 +441,30 @@ describe('platform/gitea', () => {
it('should return success if requiredStatusChecks null', async () => {
expect(await gitea.getBranchStatus('some-branch', null)).toEqual(
BRANCH_STATUS_SUCCESS
BranchStatus.green
);
});
it('should return failed if unsupported requiredStatusChecks', async () => {
expect(await gitea.getBranchStatus('some-branch', ['foo'])).toEqual(
BRANCH_STATUS_FAILED
BranchStatus.red
);
});
it('should return pending state for unknown result', async () => {
expect(await getBranchStatus('unknown')).toEqual(BRANCH_STATUS_PENDING);
it('should return yellow for unknown result', async () => {
expect(await getBranchStatus('unknown')).toEqual(BranchStatus.yellow);
});
it('should return pending state for pending result', async () => {
expect(await getBranchStatus('pending')).toEqual(BRANCH_STATUS_PENDING);
expect(await getBranchStatus('pending')).toEqual(BranchStatus.yellow);
});
it('should return success state for success result', async () => {
expect(await getBranchStatus('success')).toEqual(BRANCH_STATUS_SUCCESS);
expect(await getBranchStatus('success')).toEqual(BranchStatus.green);
});
it('should return failed state for all other results', async () => {
expect(await getBranchStatus('invalid')).toEqual(BRANCH_STATUS_FAILED);
it('should return null for all other results', async () => {
expect(await getBranchStatus('invalid')).toEqual(BranchStatus.yellow);
});
it('should abort when branch status returns 404', async () => {
@ -515,8 +510,23 @@ describe('platform/gitea', () => {
await gitea.getBranchStatusCheck('some-branch', 'some-context')
).toBeNull();
});
it('should return yellow with unknown status', async () => {
helper.getCombinedCommitStatus.mockResolvedValueOnce(
partial<ght.CombinedCommitStatus>({
statuses: [
partial<ght.CommitStatus>({
context: 'some-context',
}),
],
})
);
it('should return status of matching result', async () => {
expect(
await gitea.getBranchStatusCheck('some-branch', 'some-context')
).toEqual(BranchStatus.yellow);
});
it('should return green of matching result', async () => {
helper.getCombinedCommitStatus.mockResolvedValueOnce(
partial<ght.CombinedCommitStatus>({
statuses: [
@ -530,7 +540,7 @@ describe('platform/gitea', () => {
expect(
await gitea.getBranchStatusCheck('some-branch', 'some-context')
).toEqual('success');
).toEqual(BranchStatus.green);
});
});

View file

@ -2,7 +2,6 @@ import URL from 'url';
import GitStorage, { CommitFilesConfig, StatusResult } from '../git/storage';
import * as hostRules from '../../util/host-rules';
import {
BranchStatus,
BranchStatusConfig,
CreatePRConfig,
EnsureCommentConfig,
@ -32,11 +31,7 @@ import { RenovateConfig } from '../../config';
import { configFileNames } from '../../config/app-strings';
import { smartTruncate } from '../utils/pr-body';
import { sanitize } from '../../util/sanitize';
import {
BRANCH_STATUS_FAILED,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
import * as helper from './gitea-helper';
import { PR_STATE_ALL, PR_STATE_OPEN } from '../../constants/pull-requests';
@ -360,7 +355,7 @@ const platform: Platform = {
// Create new status for branch commit
const branchCommit = await config.storage.getBranchCommit(branchName);
await helper.createCommitStatus(config.repository, branchCommit, {
state: state ? (state as helper.CommitStatusType) : 'pending',
state: helper.renovateToGiteaStatusMapping[state] || 'pending',
context,
description,
...(target_url && { target_url }),
@ -380,12 +375,12 @@ const platform: Platform = {
requiredStatusChecks?: string[] | null
): Promise<BranchStatus> {
if (!requiredStatusChecks) {
return BRANCH_STATUS_SUCCESS;
return BranchStatus.green;
}
if (Array.isArray(requiredStatusChecks) && requiredStatusChecks.length) {
logger.warn({ requiredStatusChecks }, 'Unsupported requiredStatusChecks');
return BRANCH_STATUS_FAILED;
return BranchStatus.red;
}
let ccs: helper.CombinedCommitStatus;
@ -404,28 +399,29 @@ const platform: Platform = {
}
logger.debug({ ccs }, 'Branch status check result');
switch (ccs.worstStatus) {
case 'unknown':
case 'pending':
return BRANCH_STATUS_PENDING;
case 'success':
return BRANCH_STATUS_SUCCESS;
default:
return BRANCH_STATUS_FAILED;
}
return (
helper.giteaToRenovateStatusMapping[ccs.worstStatus] ||
BranchStatus.yellow
);
},
async getBranchStatusCheck(
branchName: string,
context: string
): Promise<string> {
): Promise<BranchStatus | null> {
const ccs = await helper.getCombinedCommitStatus(
config.repository,
branchName
);
const cs = ccs.statuses.find(s => s.context === context);
return cs ? cs.status : null;
if (!cs) return null; // no status check exists
const status = helper.giteaToRenovateStatusMapping[cs.status];
if (status) return status;
logger.warn(
{ check: cs },
'Could not map Gitea status value to Renovate status'
);
return BranchStatus.yellow;
},
async setBaseBranch(

View file

@ -72,7 +72,7 @@ Array [
"body": Object {
"context": "renovate/verify",
"description": "Renovate verified pull request",
"state": "success",
"state": "green",
"target_url": "https://github.com/renovatebot/renovate",
},
},

View file

@ -5,11 +5,7 @@ import {
REPOSITORY_NOT_FOUND,
REPOSITORY_RENAMED,
} from '../../constants/error-messages';
import {
BRANCH_STATUS_FAILED,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
import { mocked } from '../../../test/util';
describe('platform/github', () => {
@ -612,14 +608,14 @@ describe('platform/github', () => {
repository: 'some/repo',
});
const res = await github.getBranchStatus('somebranch', null);
expect(res).toEqual(BRANCH_STATUS_SUCCESS);
expect(res).toEqual(BranchStatus.green);
});
it('return failed if unsupported requiredStatusChecks', async () => {
await initRepo({
repository: 'some/repo',
});
const res = await github.getBranchStatus('somebranch', ['foo']);
expect(res).toEqual(BRANCH_STATUS_FAILED);
expect(res).toEqual(BranchStatus.red);
});
it('should pass through success', async () => {
await initRepo({
@ -634,7 +630,7 @@ describe('platform/github', () => {
} as any)
);
const res = await github.getBranchStatus('somebranch', []);
expect(res).toEqual(BRANCH_STATUS_SUCCESS);
expect(res).toEqual(BranchStatus.green);
});
it('should pass through failed', async () => {
await initRepo({
@ -649,7 +645,22 @@ describe('platform/github', () => {
} as any)
);
const res = await github.getBranchStatus('somebranch', []);
expect(res).toEqual(BRANCH_STATUS_FAILED);
expect(res).toEqual(BranchStatus.red);
});
it('defaults to pending', async () => {
await initRepo({
repository: 'some/repo',
});
api.get.mockImplementationOnce(
() =>
({
body: {
state: 'unknown',
},
} as any)
);
const res = await github.getBranchStatus('somebranch', []);
expect(res).toEqual(BranchStatus.yellow);
});
it('should fail if a check run has failed', async () => {
await initRepo({
@ -687,7 +698,7 @@ describe('platform/github', () => {
} as any)
);
const res = await github.getBranchStatus('somebranch', []);
expect(res).toEqual(BRANCH_STATUS_FAILED);
expect(res).toEqual(BranchStatus.red);
});
it('should suceed if no status and all passed check runs', async () => {
await initRepo({
@ -725,7 +736,7 @@ describe('platform/github', () => {
} as any)
);
const res = await github.getBranchStatus('somebranch', []);
expect(res).toEqual(BRANCH_STATUS_SUCCESS);
expect(res).toEqual(BranchStatus.green);
});
it('should fail if a check run has failed', async () => {
await initRepo({
@ -762,7 +773,7 @@ describe('platform/github', () => {
} as any)
);
const res = await github.getBranchStatus('somebranch', []);
expect(res).toEqual(BRANCH_STATUS_PENDING);
expect(res).toEqual(BranchStatus.yellow);
});
});
describe('getBranchStatusCheck', () => {
@ -777,15 +788,15 @@ describe('platform/github', () => {
body: [
{
context: 'context-1',
state: 'state-1',
state: 'success',
},
{
context: 'context-2',
state: 'state-2',
state: 'pending',
},
{
context: 'context-3',
state: 'state-3',
state: 'failed',
},
],
} as any)
@ -794,7 +805,7 @@ describe('platform/github', () => {
'renovate/future_branch',
'context-2'
);
expect(res).toEqual('state-2');
expect(res).toEqual(BranchStatus.yellow);
});
it('returns null', async () => {
await initRepo({
@ -806,15 +817,15 @@ describe('platform/github', () => {
body: [
{
context: 'context-1',
state: 'state-1',
state: 'success',
},
{
context: 'context-2',
state: 'state-2',
state: 'pending',
},
{
context: 'context-3',
state: 'state-3',
state: 'failed',
},
],
} as any)
@ -834,7 +845,7 @@ describe('platform/github', () => {
body: [
{
context: 'some-context',
state: 'some-state',
state: 'pending',
},
],
} as any)
@ -843,7 +854,7 @@ describe('platform/github', () => {
branchName: 'some-branch',
context: 'some-context',
description: 'some-description',
state: 'some-state',
state: BranchStatus.yellow,
url: 'some-url',
});
expect(api.post).toHaveBeenCalledTimes(0);
@ -892,7 +903,7 @@ describe('platform/github', () => {
branchName: 'some-branch',
context: 'some-context',
description: 'some-description',
state: 'some-state',
state: BranchStatus.green,
url: 'some-url',
});
expect(api.post).toHaveBeenCalledTimes(1);

View file

@ -19,7 +19,6 @@ import {
FindPRConfig,
EnsureCommentConfig,
EnsureIssueResult,
BranchStatus,
} from '../common';
import { configFileNames } from '../../config/app-strings';
@ -41,11 +40,7 @@ import {
REPOSITORY_RENAMED,
} from '../../constants/error-messages';
import { PLATFORM_TYPE_GITHUB } from '../../constants/platforms';
import {
BRANCH_STATUS_FAILED,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
import {
PR_STATE_ALL,
PR_STATE_CLOSED,
@ -1095,12 +1090,12 @@ export async function getBranchStatus(
if (!requiredStatusChecks) {
// null means disable status checks, so it always succeeds
logger.debug('Status checks disabled = returning "success"');
return BRANCH_STATUS_SUCCESS;
return BranchStatus.green;
}
if (requiredStatusChecks.length) {
// This is Unsupported
logger.warn({ requiredStatusChecks }, `Unsupported requiredStatusChecks`);
return BRANCH_STATUS_FAILED;
return BranchStatus.red;
}
let commitStatus;
try {
@ -1159,21 +1154,27 @@ export async function getBranchStatus(
}
}
if (checkRuns.length === 0) {
return commitStatus.state;
if (commitStatus.state === 'success') {
return BranchStatus.green;
}
if (commitStatus.state === 'failed') {
return BranchStatus.red;
}
return BranchStatus.yellow;
}
if (
commitStatus.state === 'failed' ||
checkRuns.some(run => run.conclusion === 'failed')
) {
return BRANCH_STATUS_FAILED;
return BranchStatus.red;
}
if (
(commitStatus.state === 'success' || commitStatus.statuses.length === 0) &&
checkRuns.every(run => ['neutral', 'success'].includes(run.conclusion))
) {
return BRANCH_STATUS_SUCCESS;
return BranchStatus.green;
}
return BRANCH_STATUS_PENDING;
return BranchStatus.yellow;
}
async function getStatusCheck(
@ -1187,15 +1188,23 @@ async function getStatusCheck(
return (await api.get(url, { useCache })).body;
}
const githubToRenovateStatusMapping = {
success: BranchStatus.green,
failed: BranchStatus.red,
pending: BranchStatus.yellow,
};
export async function getBranchStatusCheck(
branchName: string,
context: string
): Promise<string> {
): Promise<BranchStatus | null> {
try {
const res = await getStatusCheck(branchName);
for (const check of res) {
if (check.context === context) {
return check.state;
return (
githubToRenovateStatusMapping[check.state] || BranchStatus.yellow
);
}
}
return null;
@ -1696,7 +1705,7 @@ export async function createPr({
branchName,
context: `renovate/verify`,
description: `Renovate verified pull request`,
state: BRANCH_STATUS_SUCCESS,
state: BranchStatus.green,
url: 'https://github.com/renovatebot/renovate',
});
}

View file

@ -10,12 +10,7 @@ import {
PR_STATE_NOT_OPEN,
PR_STATE_OPEN,
} from '../../constants/pull-requests';
import {
BRANCH_STATUS_FAILED,
BRANCH_STATUS_FAILURE,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
import { GotResponse, Platform } from '..';
import { partial } from '../../../test/util';
@ -438,11 +433,11 @@ describe('platform/gitlab', () => {
describe('getBranchStatus(branchName, requiredStatusChecks)', () => {
it('returns success if requiredStatusChecks null', async () => {
const res = await gitlab.getBranchStatus('somebranch', null);
expect(res).toEqual(BRANCH_STATUS_SUCCESS);
expect(res).toEqual(BranchStatus.green);
});
it('return failed if unsupported requiredStatusChecks', async () => {
const res = await gitlab.getBranchStatus('somebranch', ['foo']);
expect(res).toEqual(BRANCH_STATUS_FAILED);
expect(res).toEqual(BranchStatus.red);
});
it('returns pending if no results', async () => {
await initRepo();
@ -452,7 +447,7 @@ describe('platform/gitlab', () => {
})
);
const res = await gitlab.getBranchStatus('somebranch', []);
expect(res).toEqual(BRANCH_STATUS_PENDING);
expect(res).toEqual(BranchStatus.yellow);
});
it('returns success if all are success', async () => {
await initRepo();
@ -462,7 +457,7 @@ describe('platform/gitlab', () => {
})
);
const res = await gitlab.getBranchStatus('somebranch', []);
expect(res).toEqual(BRANCH_STATUS_SUCCESS);
expect(res).toEqual(BranchStatus.green);
});
it('returns success if optional jobs fail', async () => {
await initRepo();
@ -475,7 +470,17 @@ describe('platform/gitlab', () => {
})
);
const res = await gitlab.getBranchStatus('somebranch', []);
expect(res).toEqual(BRANCH_STATUS_SUCCESS);
expect(res).toEqual(BranchStatus.green);
});
it('returns success if all are optional', async () => {
await initRepo();
api.get.mockResolvedValueOnce(
partial<GotResponse>({
body: [{ status: 'failed', allow_failure: true }],
})
);
const res = await gitlab.getBranchStatus('somebranch', []);
expect(res).toEqual(BranchStatus.green);
});
it('returns failure if any mandatory jobs fails', async () => {
await initRepo();
@ -489,9 +494,9 @@ describe('platform/gitlab', () => {
})
);
const res = await gitlab.getBranchStatus('somebranch', []);
expect(res).toEqual(BRANCH_STATUS_FAILURE);
expect(res).toEqual(BranchStatus.red);
});
it('returns custom statuses', async () => {
it('maps custom statuses to yellow', async () => {
await initRepo();
api.get.mockResolvedValueOnce(
partial<GotResponse>({
@ -499,7 +504,7 @@ describe('platform/gitlab', () => {
})
);
const res = await gitlab.getBranchStatus('somebranch', []);
expect(res).toEqual('foo');
expect(res).toEqual(BranchStatus.yellow);
});
it('throws repository-changed', async () => {
expect.assertions(1);
@ -554,21 +559,24 @@ describe('platform/gitlab', () => {
'somebranch',
'some-context'
);
expect(res).toEqual(BRANCH_STATUS_SUCCESS);
expect(res).toEqual(BranchStatus.green);
});
});
describe('setBranchStatus', () => {
it('sets branch status', async () => {
await initRepo();
await gitlab.setBranchStatus({
branchName: 'some-branch',
context: 'some-context',
description: 'some-description',
state: 'some-state',
url: 'some-url',
});
expect(api.post).toHaveBeenCalledTimes(1);
});
it.each([BranchStatus.green, BranchStatus.yellow, BranchStatus.red])(
'sets branch status yellow',
async state => {
await initRepo();
await gitlab.setBranchStatus({
branchName: 'some-branch',
context: 'some-context',
description: 'some-description',
state,
url: 'some-url',
});
expect(api.post).toHaveBeenCalledTimes(1);
}
);
});
describe('mergeBranch()', () => {
it('sends to gitFs', async () => {

View file

@ -17,7 +17,6 @@ import {
BranchStatusConfig,
FindPRConfig,
EnsureCommentConfig,
BranchStatus,
} from '../common';
import { configFileNames } from '../../config/app-strings';
import { logger } from '../../logger';
@ -36,12 +35,7 @@ import {
} from '../../constants/error-messages';
import { PR_STATE_ALL, PR_STATE_OPEN } from '../../constants/pull-requests';
import { PLATFORM_TYPE_GITLAB } from '../../constants/platforms';
import {
BRANCH_STATUS_FAILED,
BRANCH_STATUS_FAILURE,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
type MergeMethod = 'merge' | 'rebase_merge' | 'ff';
const defaultConfigFile = configFileNames[0];
@ -315,6 +309,14 @@ async function getStatus(
return (await api.get(url, { paginate: true, useCache })).body;
}
const gitlabToRenovateStatusMapping: Record<string, BranchStatus> = {
pending: BranchStatus.yellow,
running: BranchStatus.yellow,
success: BranchStatus.green,
failed: BranchStatus.red,
canceled: BranchStatus.red,
};
// Returns the combined status for a branch.
export async function getBranchStatus(
branchName: string,
@ -323,12 +325,12 @@ export async function getBranchStatus(
logger.debug(`getBranchStatus(${branchName})`);
if (!requiredStatusChecks) {
// null means disable status checks, so it always succeeds
return BRANCH_STATUS_SUCCESS;
return BranchStatus.green;
}
if (Array.isArray(requiredStatusChecks) && requiredStatusChecks.length) {
// This is Unsupported
logger.warn({ requiredStatusChecks }, `Unsupported requiredStatusChecks`);
return BRANCH_STATUS_FAILED;
return BranchStatus.red;
}
if (!(await branchExists(branchName))) {
@ -339,22 +341,29 @@ export async function getBranchStatus(
logger.debug(`Got res with ${res.length} results`);
if (res.length === 0) {
// Return 'pending' if we have no status checks
return BRANCH_STATUS_PENDING;
return BranchStatus.yellow;
}
let status: BranchStatus = BRANCH_STATUS_SUCCESS;
// Return 'success' if all are success
res.forEach(check => {
// If one is failed then don't overwrite that
if (status !== 'failure') {
if (!check.allow_failure) {
if (check.status === 'failed' || check.status === 'canceled') {
status = BRANCH_STATUS_FAILURE;
} else if (check.status !== 'success') {
({ status } = check);
let status: BranchStatus = BranchStatus.green; // default to green
res
.filter(check => !check.allow_failure)
.forEach(check => {
if (status !== BranchStatus.red) {
// if red, stay red
let mappedStatus: BranchStatus =
gitlabToRenovateStatusMapping[check.status];
if (!mappedStatus) {
logger.warn(
{ check },
'Could not map GitLab check.status to Renovate status'
);
mappedStatus = BranchStatus.yellow;
}
if (mappedStatus !== BranchStatus.green) {
logger.trace({ check }, 'Found non-green check');
status = mappedStatus;
}
}
}
});
});
return status;
}
@ -430,7 +439,7 @@ export async function getPr(iid: number): Promise<Pr> {
pr.isConflicted = true;
} else if (pr.state === PR_STATE_OPEN) {
const branchStatus = await getBranchStatus(pr.branchName, []);
if (branchStatus === BRANCH_STATUS_SUCCESS) {
if (branchStatus === BranchStatus.green) {
pr.canMerge = true;
}
}
@ -623,13 +632,13 @@ export function getRepoStatus(): Promise<StatusResult> {
export async function getBranchStatusCheck(
branchName: string,
context: string
): Promise<string | null> {
): Promise<BranchStatus | null> {
// cache-bust in case we have rebased
const res = await getStatus(branchName, false);
logger.debug(`Got res with ${res.length} results`);
for (const check of res) {
if (check.name === context) {
return check.status;
return gitlabToRenovateStatusMapping[check.status] || BranchStatus.yellow;
}
}
return null;
@ -639,15 +648,21 @@ export async function setBranchStatus({
branchName,
context,
description,
state,
state: renovateState,
url: targetUrl,
}: BranchStatusConfig): Promise<void> {
// First, get the branch commit SHA
const branchSha = await config.storage.getBranchCommit(branchName);
// Now, check the statuses for that commit
const url = `projects/${config.repository}/statuses/${branchSha}`;
let state = 'success';
if (renovateState === BranchStatus.yellow) {
state = 'pending';
} else if (renovateState === BranchStatus.red) {
state = 'failed';
}
const options: any = {
state: state.replace('failure', 'failed'), // GitLab uses 'failed', not 'failure'
state,
description,
context,
};

View file

@ -0,0 +1,5 @@
export enum BranchStatus {
green = 'green', // 'success'
yellow = 'yellow', // 'created', 'running'
red = 'red', // 'error', 'failed'
}

View file

@ -1,2 +1,3 @@
export * from './host-rules';
export * from './versioning';
export * from './branch-status';

View file

@ -1,11 +1,7 @@
import { tryBranchAutomerge } from './automerge';
import { defaultConfig, platform } from '../../../test/util';
import { RenovateConfig } from '../../config';
import {
BRANCH_STATUS_FAILURE,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
describe('workers/branch/automerge', () => {
describe('tryBranchAutomerge', () => {
@ -27,20 +23,20 @@ describe('workers/branch/automerge', () => {
it('returns false if branch status is not success', async () => {
config.automerge = true;
config.automergeType = 'branch';
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_PENDING);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.yellow);
expect(await tryBranchAutomerge(config)).toBe('no automerge');
});
it('returns branch status error if branch status is failure', async () => {
config.automerge = true;
config.automergeType = 'branch';
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_FAILURE);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.red);
expect(await tryBranchAutomerge(config)).toBe('branch status error');
});
it('returns false if PR exists', async () => {
platform.getBranchPr.mockResolvedValueOnce({} as never);
config.automerge = true;
config.automergeType = 'branch';
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_SUCCESS);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green);
expect(await tryBranchAutomerge(config)).toBe(
'automerge aborted - PR exists'
);
@ -48,7 +44,7 @@ describe('workers/branch/automerge', () => {
it('returns false if automerge fails', async () => {
config.automerge = true;
config.automergeType = 'branch';
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_SUCCESS);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green);
platform.mergeBranch.mockImplementationOnce(() => {
throw new Error('merge error');
});
@ -57,14 +53,14 @@ describe('workers/branch/automerge', () => {
it('returns true if automerge succeeds', async () => {
config.automerge = true;
config.automergeType = 'branch';
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_SUCCESS);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green);
expect(await tryBranchAutomerge(config)).toBe('automerged');
});
it('returns true if automerge succeeds (dry-run)', async () => {
config.automerge = true;
config.automergeType = 'branch';
config.dryRun = true;
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_SUCCESS);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green);
expect(await tryBranchAutomerge(config)).toBe('automerged');
});
});

View file

@ -1,11 +1,7 @@
import { logger } from '../../logger';
import { RenovateConfig } from '../../config';
import { platform } from '../../platform';
import {
BRANCH_STATUS_ERROR,
BRANCH_STATUS_FAILURE,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
export type AutomergeResult =
| 'automerged'
@ -30,7 +26,7 @@ export async function tryBranchAutomerge(
config.branchName,
config.requiredStatusChecks
);
if (branchStatus === BRANCH_STATUS_SUCCESS) {
if (branchStatus === BranchStatus.green) {
logger.debug(`Automerging branch`);
try {
if (config.dryRun)
@ -47,9 +43,7 @@ export async function tryBranchAutomerge(
logger.info({ err }, `Failed to automerge branch`);
return 'failed';
}
} else if (
[BRANCH_STATUS_FAILURE, BRANCH_STATUS_ERROR].includes(branchStatus)
) {
} else if (branchStatus === BranchStatus.red) {
return 'branch status error';
} else {
logger.debug(`Branch status is "${branchStatus}" - skipping automerge`);

View file

@ -21,7 +21,6 @@ import {
PR_STATE_MERGED,
PR_STATE_OPEN,
} from '../../constants/pull-requests';
import { BRANCH_STATUS_PENDING } from '../../constants/branch-constants';
import { StatusResult } from '../../platform/git/storage';
jest.mock('./get-updated');
@ -94,7 +93,7 @@ describe('workers/branch', () => {
config.prCreation = 'not-pending';
platform.branchExists.mockResolvedValueOnce(true);
const res = await branchWorker.processBranch(config);
expect(res).toEqual(BRANCH_STATUS_PENDING);
expect(res).toEqual('pending');
});
it('skips branch if not stabilityDays not met', async () => {
schedule.isScheduledNow.mockReturnValueOnce(true);
@ -106,7 +105,7 @@ describe('workers/branch', () => {
} as never,
];
const res = await branchWorker.processBranch(config);
expect(res).toEqual(BRANCH_STATUS_PENDING);
expect(res).toEqual('pending');
});
it('processes branch if not scheduled but updating out of schedule', async () => {
schedule.isScheduledNow.mockReturnValueOnce(false);
@ -310,9 +309,7 @@ describe('workers/branch', () => {
commit.commitFilesToBranch.mockResolvedValueOnce(null);
automerge.tryBranchAutomerge.mockResolvedValueOnce('failed');
prWorker.ensurePr.mockResolvedValueOnce('pending');
expect(await branchWorker.processBranch(config)).toEqual(
BRANCH_STATUS_PENDING
);
expect(await branchWorker.processBranch(config)).toEqual('pending');
});
it('returns if branch exists but updated', async () => {
expect.assertions(3);
@ -329,7 +326,7 @@ describe('workers/branch', () => {
requiredStatusChecks: null,
prCreation: 'not-pending',
})
).toEqual(BRANCH_STATUS_PENDING);
).toEqual('pending');
expect(automerge.tryBranchAutomerge).toHaveBeenCalledTimes(0);
expect(prWorker.ensurePr).toHaveBeenCalledTimes(0);

View file

@ -37,7 +37,7 @@ import {
PR_STATE_MERGED,
PR_STATE_OPEN,
} from '../../constants/pull-requests';
import { BRANCH_STATUS_FAILURE } from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
import { exec } from '../../util/exec';
import { regEx } from '../../util/regex';
@ -607,7 +607,7 @@ export async function processBranch(
}
const context = `renovate/artifacts`;
const description = 'Artifact file update failure';
const state = BRANCH_STATUS_FAILURE;
const state = BranchStatus.red;
const existingState = await platform.getBranchStatusCheck(
config.branchName,
context

View file

@ -5,7 +5,7 @@ import {
UnpublishableConfig,
} from './status-checks';
import { defaultConfig, platform } from '../../../test/util';
import { BRANCH_STATUS_SUCCESS } from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
describe('workers/branch/status-checks', () => {
describe('setStability', () => {
@ -23,14 +23,14 @@ describe('workers/branch/status-checks', () => {
await setStability(config);
expect(platform.getBranchStatusCheck).toHaveBeenCalledTimes(0);
});
it('sets status pending', async () => {
config.stabilityStatus = 'pending';
it('sets status yellow', async () => {
config.stabilityStatus = BranchStatus.yellow;
await setStability(config);
expect(platform.getBranchStatusCheck).toHaveBeenCalledTimes(1);
expect(platform.setBranchStatus).toHaveBeenCalledTimes(1);
});
it('sets status success', async () => {
config.stabilityStatus = 'success';
it('sets status green', async () => {
config.stabilityStatus = BranchStatus.green;
await setStability(config);
expect(platform.getBranchStatusCheck).toHaveBeenCalledTimes(1);
expect(platform.setBranchStatus).toHaveBeenCalledTimes(1);
@ -67,9 +67,7 @@ describe('workers/branch/status-checks', () => {
it('finds canBeUnpublished false and skips status', async () => {
config.unpublishSafe = true;
config.canBeUnpublished = false;
platform.getBranchStatusCheck.mockResolvedValueOnce(
BRANCH_STATUS_SUCCESS
);
platform.getBranchStatusCheck.mockResolvedValueOnce(BranchStatus.green);
await setUnpublishable(config);
expect(platform.getBranchStatusCheck).toHaveBeenCalledTimes(1);
expect(platform.setBranchStatus).toHaveBeenCalledTimes(0);

View file

@ -1,12 +1,13 @@
import { logger } from '../../logger';
import { RenovateConfig } from '../../config';
import { platform } from '../../platform';
import { BranchStatus } from '../../types';
async function setStatusCheck(
branchName: string,
context: string,
description: string,
state: string,
state: BranchStatus,
url: string
): Promise<void> {
const existingState = await platform.getBranchStatusCheck(
@ -29,7 +30,7 @@ async function setStatusCheck(
}
export type StabilityConfig = RenovateConfig & {
stabilityStatus?: string;
stabilityStatus?: BranchStatus;
branchName: string;
};
@ -39,7 +40,7 @@ export async function setStability(config: StabilityConfig): Promise<void> {
}
const context = `renovate/stability-days`;
const description =
config.stabilityStatus === 'success'
config.stabilityStatus === BranchStatus.green
? 'Updates have met stability days requirement'
: 'Updates have not met stability days requirement';
await setStatusCheck(
@ -65,7 +66,9 @@ export async function setUnpublishable(
}
const context = `renovate/unpublish-safe`;
// Set canBeUnpublished status check
const state = config.canBeUnpublished ? 'pending' : 'success';
const state = config.canBeUnpublished
? BranchStatus.yellow
: BranchStatus.green;
const description = config.canBeUnpublished
? 'Packages < 24 hours old can be unpublished'
: 'Packages cannot be unpublished';

View file

@ -1,7 +1,7 @@
import { platform } from '../../../platform';
import { emojify } from '../../../util/emoji';
import { BranchConfig } from '../../common';
import { BRANCH_STATUS_FAILED } from '../../../constants/branch-constants';
import { BranchStatus } from '../../../types';
export async function getPrConfigDescription(
config: BranchConfig
@ -31,7 +31,7 @@ export async function getPrConfigDescription(
config.requiredStatusChecks
);
// istanbul ignore if
if (branchStatus === BRANCH_STATUS_FAILED) {
if (branchStatus === BranchStatus.red) {
prBody += 'Disabled due to failing status checks.';
} else {
prBody += 'Enabled.';

View file

@ -3,11 +3,7 @@ import * as _changelogHelper from './changelog';
import { getConfig } from '../../config/defaults';
import { platform as _platform, Pr } from '../../platform';
import { mocked } from '../../../test/util';
import {
BRANCH_STATUS_FAILED,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
import { PLATFORM_TYPE_GITLAB } from '../../constants/platforms';
const changelogHelper = mocked(_changelogHelper);
@ -78,7 +74,7 @@ describe('workers/pr', () => {
it('should automerge if enabled and pr is mergeable', async () => {
config.automerge = true;
pr.isModified = false;
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_SUCCESS);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green);
platform.mergePr.mockResolvedValueOnce(true);
await prWorker.checkAutoMerge(pr, config);
expect(platform.mergePr).toHaveBeenCalledTimes(1);
@ -88,20 +84,20 @@ describe('workers/pr', () => {
config.automergeType = 'pr-comment';
config.automergeComment = '!merge';
pr.isModified = false;
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_SUCCESS);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green);
await prWorker.checkAutoMerge(pr, config);
expect(platform.ensureComment).toHaveBeenCalledTimes(1);
});
it('should not automerge if enabled and pr is mergeable but cannot rebase', async () => {
config.automerge = true;
pr.isModified = true;
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_SUCCESS);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green);
await prWorker.checkAutoMerge(pr, config);
expect(platform.mergePr).toHaveBeenCalledTimes(0);
});
it('should not automerge if enabled and pr is mergeable but branch status is not success', async () => {
config.automerge = true;
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_PENDING);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.yellow);
await prWorker.checkAutoMerge(pr, config);
expect(platform.mergePr).toHaveBeenCalledTimes(0);
});
@ -167,19 +163,19 @@ describe('workers/pr', () => {
expect(pr).toBeNull();
});
it('should return null if waiting for success', async () => {
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_FAILED);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.red);
config.prCreation = 'status-success';
const pr = await prWorker.ensurePr(config);
expect(pr).toEqual('pending');
});
it('should return needs-approval if prCreation set to approval', async () => {
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_SUCCESS);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green);
config.prCreation = 'approval';
const pr = await prWorker.ensurePr(config);
expect(pr).toBe('needs-pr-approval');
});
it('should create PR if success', async () => {
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_SUCCESS);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green);
config.prCreation = 'status-success';
config.automerge = true;
config.schedule = 'before 5am';
@ -221,7 +217,7 @@ describe('workers/pr', () => {
expect(platform.createPr.mock.calls[0]).toMatchSnapshot();
});
it('should add note about Pin', async () => {
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_SUCCESS);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green);
config.prCreation = 'status-success';
config.isPin = true;
config.updateType = 'pin';
@ -236,7 +232,7 @@ describe('workers/pr', () => {
).toBe(true);
});
it('should return null if creating PR fails', async () => {
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_SUCCESS);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green);
platform.createPr = jest.fn();
platform.createPr.mockImplementationOnce(() => {
throw new Error('Validation Failed (422)');
@ -246,7 +242,7 @@ describe('workers/pr', () => {
expect(pr).toBeNull();
});
it('should return null if waiting for not pending', async () => {
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_PENDING);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.yellow);
platform.getBranchLastCommitTime.mockImplementationOnce(() =>
Promise.resolve(new Date())
);
@ -255,7 +251,7 @@ describe('workers/pr', () => {
expect(pr).toEqual('pending');
});
it('should create PR if pending timeout hit', async () => {
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_PENDING);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.yellow);
platform.getBranchLastCommitTime.mockImplementationOnce(() =>
Promise.resolve(new Date('2017-01-01'))
);
@ -264,7 +260,7 @@ describe('workers/pr', () => {
expect(pr).toMatchObject({ displayNumber: 'New Pull Request' });
});
it('should create PR if no longer pending', async () => {
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_FAILED);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.red);
config.prCreation = 'not-pending';
const pr = await prWorker.ensurePr(config);
expect(pr).toMatchObject({ displayNumber: 'New Pull Request' });
@ -305,7 +301,7 @@ describe('workers/pr', () => {
expect(platform.addAssignees).toHaveBeenCalledTimes(1);
expect(platform.addReviewers).toHaveBeenCalledTimes(1);
});
it('should not add assignees and reviewers to new PR if automerging enabled', async () => {
it('should not add assignees and reviewers to new PR if automerging enabled regularly', async () => {
config.assignees = ['bar'];
config.reviewers = ['baz'];
config.automerge = true;
@ -394,14 +390,14 @@ describe('workers/pr', () => {
config.automerge = true;
config.automergeType = 'branch';
config.branchAutomergeFailureMessage = 'branch status error';
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_FAILED);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.red);
const pr = await prWorker.ensurePr(config);
expect(pr).toMatchObject({ displayNumber: 'New Pull Request' });
});
it('should create PR if branch automerging failed', async () => {
config.automerge = true;
config.automergeType = 'branch';
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_SUCCESS);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green);
config.forcePr = true;
const pr = await prWorker.ensurePr(config);
expect(pr).toMatchObject({ displayNumber: 'New Pull Request' });
@ -409,7 +405,7 @@ describe('workers/pr', () => {
it('should return null if branch automerging not failed', async () => {
config.automerge = true;
config.automergeType = 'branch';
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_PENDING);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.yellow);
platform.getBranchLastCommitTime.mockResolvedValueOnce(new Date());
const pr = await prWorker.ensurePr(config);
expect(pr).toBeNull();
@ -417,7 +413,7 @@ describe('workers/pr', () => {
it('should not return null if branch automerging taking too long', async () => {
config.automerge = true;
config.automergeType = 'branch';
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_PENDING);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.yellow);
platform.getBranchLastCommitTime.mockResolvedValueOnce(
new Date('2018-01-01')
);
@ -430,7 +426,7 @@ describe('workers/pr', () => {
expect(pr).toMatchObject({ displayNumber: 'New Pull Request' });
});
it('should create privateRepo PR if success', async () => {
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_SUCCESS);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.green);
config.prCreation = 'status-success';
config.privateRepo = false;
const pr = await prWorker.ensurePr(config);
@ -439,7 +435,7 @@ describe('workers/pr', () => {
existingPr.body = platform.createPr.mock.calls[0][0].prBody;
});
it('should create PR if waiting for not pending but artifactErrors', async () => {
platform.getBranchStatus.mockResolvedValueOnce(BRANCH_STATUS_PENDING);
platform.getBranchStatus.mockResolvedValueOnce(BranchStatus.yellow);
platform.getBranchLastCommitTime.mockResolvedValueOnce(new Date());
config.prCreation = 'not-pending';
config.artifactErrors = [{}];

View file

@ -3,7 +3,7 @@ import uniq from 'lodash/uniq';
import { logger } from '../../logger';
import { ChangeLogError, getChangeLogJSON } from './changelog';
import { getPrBody } from './body';
import { BranchStatus, platform, Pr, PlatformPrOptions } from '../../platform';
import { platform, Pr, PlatformPrOptions } from '../../platform';
import { BranchConfig } from '../common';
import {
PLATFORM_FAILURE,
@ -11,15 +11,7 @@ import {
PLATFORM_RATE_LIMIT_EXCEEDED,
REPOSITORY_CHANGED,
} from '../../constants/error-messages';
import {
BRANCH_STATUS_CREATED,
BRANCH_STATUS_ERROR,
BRANCH_STATUS_FAILED,
BRANCH_STATUS_FAILURE,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_RUNNING,
BRANCH_STATUS_SUCCESS,
} from '../../constants/branch-constants';
import { BranchStatus } from '../../types';
function noWhitespace(input: string): string {
return input.replace(/\r?\n|\r|\s/g, '');
@ -121,13 +113,7 @@ export async function ensurePr(
logger.debug(
`Branch is configured for branch automerge, branch status) is: ${await getBranchStatus()}`
);
if (
[
BRANCH_STATUS_CREATED,
BRANCH_STATUS_PENDING,
BRANCH_STATUS_RUNNING,
].includes(await getBranchStatus())
) {
if ((await getBranchStatus()) === BranchStatus.yellow) {
logger.debug('Checking how long this branch has been pending');
const lastCommitTime = await platform.getBranchLastCommitTime(branchName);
const currentTime = new Date();
@ -140,14 +126,7 @@ export async function ensurePr(
config.forcePr = true;
}
}
if (
config.forcePr ||
[
BRANCH_STATUS_FAILURE,
BRANCH_STATUS_ERROR,
BRANCH_STATUS_FAILED,
].includes(await getBranchStatus())
) {
if (config.forcePr || (await getBranchStatus()) === BranchStatus.red) {
logger.debug(`Branch tests failed, so will create PR`);
} else {
return null;
@ -155,11 +134,11 @@ export async function ensurePr(
}
if (config.prCreation === 'status-success') {
logger.debug('Checking branch combined status');
if ((await getBranchStatus()) !== BRANCH_STATUS_SUCCESS) {
if ((await getBranchStatus()) !== BranchStatus.green) {
logger.debug(
`Branch status is "${await getBranchStatus()}" - not creating PR`
);
return BRANCH_STATUS_PENDING;
return 'pending';
}
logger.debug('Branch status success');
} else if (
@ -174,10 +153,7 @@ export async function ensurePr(
!config.forcePr
) {
logger.debug('Checking branch combined status');
if (
(await getBranchStatus()) === BRANCH_STATUS_PENDING ||
(await getBranchStatus()) === BRANCH_STATUS_RUNNING
) {
if ((await getBranchStatus()) === BranchStatus.yellow) {
logger.debug(
`Branch status is "${await getBranchStatus()}" - checking timeout`
);
@ -191,7 +167,7 @@ export async function ensurePr(
logger.debug(
`Branch is ${elapsedHours} hours old - skipping PR creation`
);
return BRANCH_STATUS_PENDING;
return 'pending';
}
logger.debug(
`prNotPendingHours=${config.prNotPendingHours} threshold hit - creating PR`
@ -274,14 +250,7 @@ export async function ensurePr(
if (existingPr) {
logger.debug('Processing existing PR');
// istanbul ignore if
if (
config.automerge &&
[
BRANCH_STATUS_FAILURE,
BRANCH_STATUS_ERROR,
BRANCH_STATUS_FAILED,
].includes(await getBranchStatus())
) {
if (config.automerge && (await getBranchStatus()) === BranchStatus.red) {
logger.debug(`Setting assignees and reviewers as status checks failed`);
await addAssigneesReviewers(config, existingPr);
}
@ -416,11 +385,7 @@ export async function ensurePr(
if (
config.automerge &&
!config.assignAutomerge &&
![
BRANCH_STATUS_FAILURE,
BRANCH_STATUS_ERROR,
BRANCH_STATUS_FAILED,
].includes(await getBranchStatus())
(await getBranchStatus()) !== BranchStatus.red
) {
logger.debug(
`Skipping assignees and reviewers as automerge=${config.automerge}`
@ -478,7 +443,7 @@ export async function checkAutoMerge(pr: Pr, config): Promise<boolean> {
branchName,
requiredStatusChecks
);
if (branchStatus !== BRANCH_STATUS_SUCCESS) {
if (branchStatus !== BranchStatus.green) {
logger.debug(
`PR is not ready for merge (branch status is ${branchStatus})`
);

View file

@ -1,10 +1,7 @@
import { PR_STATE_OPEN } from '../../../constants/pull-requests';
import * as validate from './validate';
import { platform } from '../../../../test/util';
import {
BRANCH_STATUS_FAILURE,
BRANCH_STATUS_SUCCESS,
} from '../../../constants/branch-constants';
import { BranchStatus } from '../../../types';
beforeEach(() => {
jest.resetAllMocks();
@ -55,7 +52,7 @@ describe('workers/repository/validate', () => {
await validate.validatePrs({});
expect(platform.setBranchStatus).toHaveBeenCalledTimes(1);
expect(platform.setBranchStatus.mock.calls[0][0].state).toEqual(
BRANCH_STATUS_FAILURE
BranchStatus.red
);
expect(platform.ensureComment).toHaveBeenCalledTimes(1);
expect(platform.ensureCommentRemoval).toHaveBeenCalledTimes(0);
@ -73,7 +70,7 @@ describe('workers/repository/validate', () => {
await validate.validatePrs({});
expect(platform.setBranchStatus).toHaveBeenCalledTimes(1);
expect(platform.setBranchStatus.mock.calls[0][0].state).toEqual(
BRANCH_STATUS_FAILURE
BranchStatus.red
);
expect(platform.ensureComment).toHaveBeenCalledTimes(1);
expect(platform.ensureCommentRemoval).toHaveBeenCalledTimes(0);
@ -91,7 +88,7 @@ describe('workers/repository/validate', () => {
await validate.validatePrs({});
expect(platform.setBranchStatus).toHaveBeenCalledTimes(1);
expect(platform.setBranchStatus.mock.calls[0][0].state).toEqual(
BRANCH_STATUS_SUCCESS
BranchStatus.green
);
expect(platform.ensureComment).toHaveBeenCalledTimes(0);
expect(platform.ensureCommentRemoval).toHaveBeenCalledTimes(1);

View file

@ -6,10 +6,7 @@ import { platform, Pr } from '../../../platform';
import { RenovateConfig } from '../../../config';
import { PR_STATE_OPEN } from '../../../constants/pull-requests';
import { REPOSITORY_CHANGED } from '../../../constants/error-messages';
import {
BRANCH_STATUS_FAILURE,
BRANCH_STATUS_SUCCESS,
} from '../../../constants/branch-constants';
import { BranchStatus } from '../../../types';
async function getRenovatePrs(branchPrefix: string): Promise<Pr[]> {
return (await platform.getPrList())
@ -91,7 +88,7 @@ export async function validatePrs(config: RenovateConfig): Promise<void> {
}
}
// if the PR has renovate files then we set a status no matter what
let status: 'failure' | 'success';
let status: BranchStatus;
let description: string;
const topic = `Renovate Configuration Errors`;
if (validations.length) {
@ -103,11 +100,11 @@ export async function validatePrs(config: RenovateConfig): Promise<void> {
topic,
content,
});
status = BRANCH_STATUS_FAILURE;
status = BranchStatus.red;
description = `Renovate config validation failed`; // GitHub limit
} else {
description = `Renovate config is valid`;
status = BRANCH_STATUS_SUCCESS;
status = BranchStatus.green;
await platform.ensureCommentRemoval(pr.number, topic);
}
// istanbul ignore else