2020-05-01 16:03:48 +00:00
import is from '@sindresorhus/is' ;
2020-04-22 11:18:15 +00:00
import { concat } from 'lodash' ;
2020-05-01 16:03:48 +00:00
import { DateTime } from 'luxon' ;
import minimatch from 'minimatch' ;
2019-10-18 12:30:51 +00:00
import { RenovateConfig } from '../../config' ;
2020-01-12 07:50:11 +00:00
import {
2020-05-01 16:03:48 +00:00
DATASOURCE_FAILURE ,
MANAGER_LOCKFILE_ERROR ,
2020-01-12 07:50:11 +00:00
PLATFORM_AUTHENTICATION_ERROR ,
PLATFORM_BAD_CREDENTIALS ,
2020-05-01 16:03:48 +00:00
PLATFORM_FAILURE ,
2020-01-12 07:50:11 +00:00
PLATFORM_INTEGRATION_UNAUTHORIZED ,
PLATFORM_RATE_LIMIT_EXCEEDED ,
REPOSITORY_CHANGED ,
2020-05-01 16:03:48 +00:00
SYSTEM_INSUFFICIENT_DISK_SPACE ,
2020-01-12 07:50:11 +00:00
WORKER_FILE_UPDATE_FAILED ,
} from '../../constants/error-messages' ;
2020-03-05 06:03:47 +00:00
import {
PR_STATE_CLOSED ,
PR_STATE_MERGED ,
PR_STATE_OPEN ,
} from '../../constants/pull-requests' ;
2020-05-01 16:03:48 +00:00
import { logger } from '../../logger' ;
2020-05-05 17:46:35 +00:00
import { getAdditionalFiles } from '../../manager/npm/post-update' ;
2020-05-01 16:03:48 +00:00
import { platform } from '../../platform' ;
2020-03-08 14:03:19 +00:00
import { BranchStatus } from '../../types' ;
2020-05-01 16:03:48 +00:00
import { emojify } from '../../util/emoji' ;
2020-02-04 05:59:13 +00:00
import { exec } from '../../util/exec' ;
2020-04-22 11:18:15 +00:00
import { readLocalFile , writeLocalFile } from '../../util/fs' ;
2020-05-01 16:03:48 +00:00
import { regEx } from '../../util/regex' ;
import { BranchConfig , PrResult , ProcessBranchResult } from '../common' ;
import { checkAutoMerge , ensurePr } from '../pr' ;
import { tryBranchAutomerge } from './automerge' ;
import { prAlreadyExisted } from './check-existing' ;
import { commitFilesToBranch } from './commit' ;
import { getUpdatedPackageFiles } from './get-updated' ;
2020-05-15 10:45:03 +00:00
import { shouldReuseExistingBranch } from './reuse' ;
2020-05-01 16:03:48 +00:00
import { isScheduledNow } from './schedule' ;
import { setStability , setUnpublishable } from './status-checks' ;
2017-02-14 07:08:40 +00:00
2019-11-24 04:09:13 +00:00
// TODO: proper typings
function rebaseCheck ( config : RenovateConfig , branchPr : any ) : boolean {
const titleRebase = branchPr . title && branchPr . title . startsWith ( 'rebase!' ) ;
const labelRebase =
branchPr . labels && branchPr . labels . includes ( config . rebaseLabel ) ;
const prRebaseChecked =
2019-12-05 09:45:28 +00:00
branchPr . body && branchPr . body . includes ( ` - [x] <!-- rebase-check --> ` ) ;
2019-11-24 04:09:13 +00:00
return titleRebase || labelRebase || prRebaseChecked ;
}
2019-10-18 12:30:51 +00:00
export async function processBranch (
branchConfig : BranchConfig ,
2020-05-05 17:46:35 +00:00
prHourlyLimitReached? : boolean
2019-10-18 12:30:51 +00:00
) : Promise < ProcessBranchResult > {
2019-12-09 11:42:55 +00:00
const config : BranchConfig = { . . . branchConfig } ;
2017-12-15 20:33:54 +00:00
const dependencies = config . upgrades
2020-04-12 16:09:36 +00:00
. map ( ( upgrade ) = > upgrade . depName )
. filter ( ( v ) = > v ) // remove nulls (happens for lock file maintenance)
2017-12-15 20:33:54 +00:00
. filter ( ( value , i , list ) = > list . indexOf ( value ) === i ) ; // remove duplicates
2019-08-10 06:11:58 +00:00
logger . debug (
{ dependencies } ,
` processBranch with ${ branchConfig . upgrades . length } upgrades `
) ;
logger . trace ( { config } , 'branch config' ) ;
2018-01-12 06:47:18 +00:00
await platform . setBaseBranch ( config . baseBranch ) ;
2018-01-11 10:49:01 +00:00
const branchExists = await platform . branchExists ( config . branchName ) ;
2018-10-03 14:01:57 +00:00
const branchPr = await platform . getBranchPr ( config . branchName ) ;
2018-01-11 10:49:01 +00:00
logger . debug ( ` branchExists= ${ branchExists } ` ) ;
2018-10-04 08:07:59 +00:00
const masterIssueCheck = ( config . masterIssueChecks || { } ) [ config . branchName ] ;
// istanbul ignore if
if ( masterIssueCheck ) {
2020-02-24 07:43:01 +00:00
logger . debug (
'Branch has been checked in master issue: ' + masterIssueCheck
) ;
2018-10-04 08:07:59 +00:00
}
2019-06-30 07:17:16 +00:00
if ( branchPr ) {
config . rebaseRequested = rebaseCheck ( config , branchPr ) ;
logger . debug ( ` Branch pr rebase requested: ${ config . rebaseRequested } ` ) ;
}
2017-08-26 14:10:18 +00:00
try {
2019-05-24 13:01:07 +00:00
logger . debug ( ` Branch has ${ dependencies . length } upgrade(s) ` ) ;
2017-06-29 17:50:26 +00:00
2017-11-10 12:32:33 +00:00
// Check if branch already existed
2018-10-03 14:01:57 +00:00
const existingPr = branchPr ? undefined : await prAlreadyExisted ( config ) ;
2018-10-04 08:07:59 +00:00
if ( existingPr && ! masterIssueCheck ) {
2018-09-11 07:11:59 +00:00
logger . debug (
2017-10-18 19:39:10 +00:00
{ prTitle : config.prTitle } ,
'Closed PR already exists. Skipping branch.'
) ;
2020-03-05 06:03:47 +00:00
if ( existingPr . state === PR_STATE_CLOSED ) {
2020-01-14 11:13:35 +00:00
const topic = ` Renovate Ignore Notification ` ;
2018-01-20 14:03:57 +00:00
let content ;
2018-07-04 08:11:53 +00:00
if ( config . updateType === 'major' ) {
2019-12-05 10:55:14 +00:00
content = ` As this PR has been closed unmerged, Renovate will ignore this upgrade and you will not receive PRs for *any* future ${ config . newMajor } .x releases. However, if you upgrade to ${ config . newMajor } .x manually then Renovate will then reenable updates for minor and patch updates automatically. ` ;
2018-07-04 08:11:53 +00:00
} else if ( config . updateType === 'digest' ) {
2019-12-05 10:55:14 +00:00
content = ` As this PR has been closed unmerged, Renovate will ignore this upgrade updateType and you will not receive PRs for *any* future ${ config . depName } : ${ config . currentValue } digest updates. Digest updates will resume if you update the specified tag at any time. ` ;
2018-01-20 14:03:57 +00:00
} else {
2019-12-05 10:55:14 +00:00
content = ` As this PR has been closed unmerged, Renovate will now ignore this update ( ${ config . newValue } ). You will still receive a PR once a newer version is released, so if you wish to permanently ignore this dependency, please add it to the \` ignoreDeps \` array of your renovate config. ` ;
2018-01-20 14:03:57 +00:00
}
content +=
2018-01-23 09:50:05 +00:00
'\n\nIf this PR was closed by mistake or you changed your mind, you can simply rename this PR and you will soon get a fresh replacement PR opened.' ;
2018-12-08 11:42:56 +00:00
if ( ! config . suppressNotifications . includes ( 'prIgnoreNotification' ) ) {
if ( config . dryRun ) {
logger . info (
'DRY-RUN: Would ensure closed PR comment in PR #' +
existingPr . number
) ;
} else {
2020-01-14 11:13:35 +00:00
await platform . ensureComment ( {
number : existingPr . number ,
topic ,
content ,
} ) ;
2018-12-08 11:42:56 +00:00
}
2018-10-26 07:48:49 +00:00
}
2018-01-20 14:03:57 +00:00
if ( branchExists ) {
2018-10-26 07:48:49 +00:00
if ( config . dryRun ) {
logger . info ( 'DRY-RUN: Would delete branch ' + config . branchName ) ;
} else {
await platform . deleteBranch ( config . branchName ) ;
}
2018-01-20 14:03:57 +00:00
}
2020-03-05 06:03:47 +00:00
} else if ( existingPr . state === PR_STATE_MERGED ) {
2020-02-24 11:31:06 +00:00
logger . debug (
2018-05-30 09:03:30 +00:00
{ pr : existingPr.number } ,
'Merged PR is blocking this branch'
) ;
2018-01-10 09:26:57 +00:00
}
2017-08-28 13:50:11 +00:00
return 'already-existed' ;
2017-08-06 13:38:10 +00:00
}
2018-10-04 09:22:31 +00:00
// istanbul ignore if
if ( ! branchExists && config . masterIssueApproval ) {
if ( masterIssueCheck ) {
2020-02-24 07:43:01 +00:00
logger . debug ( ` Branch ${ config . branchName } is approved for creation ` ) ;
2018-10-04 09:22:31 +00:00
} else {
2020-02-24 07:43:01 +00:00
logger . debug ( ` Branch ${ config . branchName } needs approval ` ) ;
2018-10-04 09:22:31 +00:00
return 'needs-approval' ;
}
}
2019-05-19 05:01:25 +00:00
if (
! branchExists &&
prHourlyLimitReached &&
! masterIssueCheck &&
! config . vulnerabilityAlert
) {
2020-02-24 07:43:01 +00:00
logger . debug (
2019-10-14 11:15:37 +00:00
'Reached PR creation limit or per run commits limit - skipping branch creation'
) ;
2018-10-03 14:00:58 +00:00
return 'pr-hourly-limit-reached' ;
}
2018-02-28 03:43:30 +00:00
if ( branchExists ) {
logger . debug ( 'Checking if PR has been edited' ) ;
2018-05-30 09:03:30 +00:00
if ( branchPr ) {
2018-06-01 17:33:52 +00:00
logger . debug ( 'Found existing branch PR' ) ;
2020-03-05 06:03:47 +00:00
if ( branchPr . state !== PR_STATE_OPEN ) {
2020-02-24 07:43:01 +00:00
logger . debug (
2018-06-26 09:20:26 +00:00
'PR has been closed or merged since this run started - aborting'
) ;
2020-01-12 07:50:11 +00:00
throw new Error ( REPOSITORY_CHANGED ) ;
2018-06-26 09:20:26 +00:00
}
2020-05-27 05:13:54 +00:00
const topic = 'PR has been edited' ;
2019-08-14 17:48:31 +00:00
if (
2019-08-29 08:30:17 +00:00
branchPr . isModified ||
2019-08-14 17:48:31 +00:00
( branchPr . targetBranch &&
branchPr . targetBranch !== branchConfig . baseBranch )
) {
2020-05-27 05:13:54 +00:00
logger . debug ( { prNo : branchPr.number } , 'PR has been edited' ) ;
2019-06-30 07:17:16 +00:00
if ( masterIssueCheck || config . rebaseRequested ) {
2018-10-26 07:48:49 +00:00
if ( config . dryRun ) {
logger . info (
'DRY-RUN: Would ensure PR edited comment removal in PR #' +
branchPr . number
) ;
} else {
2020-06-09 11:42:22 +00:00
// Remove any "PR has been edited" comment only when rebasing
2020-05-03 19:03:55 +00:00
await platform . ensureCommentRemoval ( {
number : branchPr . number ,
topic ,
} ) ;
2018-10-26 07:48:49 +00:00
}
2018-09-20 14:10:14 +00:00
} else {
2019-09-25 09:40:16 +00:00
let content = emojify (
2019-12-05 10:55:14 +00:00
` :construction_worker: This PR has received other commits, so Renovate will stop updating it to avoid conflicts or other problems. `
2019-09-25 09:40:16 +00:00
) ;
2019-12-05 10:55:14 +00:00
content += ` If you wish to abandon your changes and have Renovate start over you may click the "rebase" checkbox in the PR body/description. ` ;
2020-06-09 11:42:22 +00:00
content += ` \ n \ nIf you think this comment is in error and the branch is *not* modified, try deleting this comment. If it comes back again the next time Renovate runs, please submit an issue or seek config help. ` ;
2018-12-08 11:42:56 +00:00
if ( ! config . suppressNotifications . includes ( 'prEditNotification' ) ) {
if ( config . dryRun ) {
logger . info (
'DRY-RUN: ensure comment in PR #' + branchPr . number
) ;
} else {
2020-01-14 11:13:35 +00:00
await platform . ensureComment ( {
number : branchPr . number ,
topic ,
content ,
} ) ;
2018-12-08 11:42:56 +00:00
}
2018-10-26 07:48:49 +00:00
}
2018-09-20 14:10:14 +00:00
return 'pr-edited' ;
}
2017-12-28 15:29:03 +00:00
}
2017-12-27 14:20:32 +00:00
}
}
2017-11-10 12:32:33 +00:00
// Check schedule
config . isScheduledNow = isScheduledNow ( config ) ;
2018-10-04 08:07:59 +00:00
if ( ! config . isScheduledNow && ! masterIssueCheck ) {
2018-01-11 10:49:01 +00:00
if ( ! branchExists ) {
2020-02-24 07:43:01 +00:00
logger . debug ( 'Skipping branch creation as not within schedule' ) ;
2017-11-10 12:32:33 +00:00
return 'not-scheduled' ;
}
2019-06-30 07:17:16 +00:00
if ( config . updateNotScheduled === false && ! config . rebaseRequested ) {
2017-11-10 12:32:33 +00:00
logger . debug ( 'Skipping branch update as not within schedule' ) ;
return 'not-scheduled' ;
}
2018-09-13 07:08:25 +00:00
// istanbul ignore if
2018-09-19 05:07:04 +00:00
if ( ! branchPr ) {
logger . debug ( 'Skipping PR creation out of schedule' ) ;
2018-09-13 07:08:25 +00:00
return 'not-scheduled' ;
}
2017-11-10 12:32:33 +00:00
logger . debug (
2018-09-19 05:07:04 +00:00
'Branch + PR exists but is not scheduled -- will update if necessary'
2017-11-10 12:32:33 +00:00
) ;
}
2018-03-21 10:40:13 +00:00
if (
2018-07-04 08:11:53 +00:00
config . updateType !== 'lockFileMaintenance' &&
2018-03-21 10:40:13 +00:00
config . unpublishSafe &&
2018-06-04 12:56:47 +00:00
config . canBeUnpublished &&
2018-03-21 10:40:13 +00:00
( config . prCreation === 'not-pending' ||
2019-05-24 13:01:07 +00:00
/* istanbul ignore next */ config . prCreation === 'status-success' )
2018-03-21 10:40:13 +00:00
) {
2020-02-24 07:43:01 +00:00
logger . debug (
2018-03-21 10:40:13 +00:00
'Skipping branch creation due to unpublishSafe + status checks'
) ;
return 'pending' ;
}
2019-08-26 08:32:59 +00:00
if (
config . upgrades . some (
2020-04-12 16:09:36 +00:00
( upgrade ) = > upgrade . stabilityDays && upgrade . releaseTimestamp
2019-08-26 08:32:59 +00:00
)
) {
// Only set a stability status check if one or more of the updates contain
// both a stabilityDays setting and a releaseTimestamp
2020-03-10 11:00:36 +00:00
config . stabilityStatus = BranchStatus . green ;
2019-08-26 08:32:59 +00:00
// Default to 'success' but set 'pending' if any update is pending
const oneDay = 24 * 60 * 60 * 1000 ;
for ( const upgrade of config . upgrades ) {
if ( upgrade . stabilityDays && upgrade . releaseTimestamp ) {
const daysElapsed = Math . floor (
( new Date ( ) . getTime ( ) -
new Date ( upgrade . releaseTimestamp ) . getTime ( ) ) /
oneDay
) ;
2019-12-13 14:56:21 +00:00
if ( ! masterIssueCheck && daysElapsed < upgrade . stabilityDays ) {
2019-08-26 08:32:59 +00:00
logger . debug (
{
depName : upgrade.depName ,
daysElapsed ,
stabilityDays : upgrade.stabilityDays ,
} ,
'Update has not passed stability days'
) ;
2020-03-10 11:00:36 +00:00
config . stabilityStatus = BranchStatus . yellow ;
2019-08-26 08:32:59 +00:00
}
}
}
// Don't create a branch if we know it will be status 'pending'
if (
2019-12-13 14:56:21 +00:00
! masterIssueCheck &&
2019-08-26 08:32:59 +00:00
! branchExists &&
2020-03-10 11:00:36 +00:00
config . stabilityStatus === BranchStatus . yellow &&
2019-08-26 08:32:59 +00:00
[ 'not-pending' , 'status-success' ] . includes ( config . prCreation )
) {
2020-02-24 07:43:01 +00:00
logger . debug ( 'Skipping branch creation due to stability days not met' ) ;
2019-08-26 08:32:59 +00:00
return 'pending' ;
}
}
2018-10-04 08:07:59 +00:00
// istanbul ignore if
2019-04-28 07:04:50 +00:00
if ( masterIssueCheck === 'rebase' || config . masterIssueRebaseAllOpen ) {
2020-02-24 07:43:01 +00:00
logger . debug ( 'Manual rebase requested via master issue' ) ;
2020-05-15 10:45:03 +00:00
config . reuseExistingBranch = false ;
2018-10-04 08:07:59 +00:00
} else {
2020-05-15 10:45:03 +00:00
Object . assign ( config , await shouldReuseExistingBranch ( config ) ) ;
2018-10-04 08:07:59 +00:00
}
2020-05-15 10:45:03 +00:00
logger . debug ( ` Using reuseExistingBranch: ${ config . reuseExistingBranch } ` ) ;
2018-10-01 16:15:06 +00:00
const res = await getUpdatedPackageFiles ( config ) ;
// istanbul ignore if
2019-02-08 13:31:30 +00:00
if ( res . artifactErrors && config . artifactErrors ) {
res . artifactErrors = config . artifactErrors . concat ( res . artifactErrors ) ;
2018-10-01 16:15:06 +00:00
}
Object . assign ( config , res ) ;
2018-02-11 18:58:59 +00:00
if ( config . updatedPackageFiles && config . updatedPackageFiles . length ) {
2017-08-26 14:10:18 +00:00
logger . debug (
` Updated ${ config . updatedPackageFiles . length } package files `
2017-04-21 08:12:41 +00:00
) ;
2017-08-26 14:10:18 +00:00
} else {
logger . debug ( 'No package files need updating' ) ;
2017-04-15 18:32:01 +00:00
}
2020-05-05 17:46:35 +00:00
const additionalFiles = await getAdditionalFiles (
config ,
branchConfig . packageFiles
) ;
2019-02-08 13:31:30 +00:00
config . artifactErrors = ( config . artifactErrors || [ ] ) . concat (
additionalFiles . artifactErrors
2018-10-01 16:15:06 +00:00
) ;
2019-02-08 13:31:30 +00:00
config . updatedArtifacts = ( config . updatedArtifacts || [ ] ) . concat (
additionalFiles . updatedArtifacts
2018-07-18 18:10:50 +00:00
) ;
2019-02-08 13:31:30 +00:00
if ( config . updatedArtifacts && config . updatedArtifacts . length ) {
2017-08-26 14:10:18 +00:00
logger . debug (
2019-05-01 07:33:11 +00:00
{
2020-04-12 16:09:36 +00:00
updatedArtifacts : config.updatedArtifacts.map ( ( f ) = >
2019-05-01 07:33:11 +00:00
f . name === '|delete|' ? ` ${ f . contents } (delete) ` : f . name
) ,
} ,
2019-02-08 13:31:30 +00:00
` Updated ${ config . updatedArtifacts . length } lock files `
2017-08-08 21:03:52 +00:00
) ;
2017-08-26 14:10:18 +00:00
} else {
logger . debug ( 'No updated lock files in branch' ) ;
2017-08-08 21:03:52 +00:00
}
2020-02-04 05:59:13 +00:00
if (
2020-04-22 11:18:15 +00:00
/* Only run post-upgrade tasks if there are changes to package files... */
( config . updatedPackageFiles ? . length > 0 ||
/* ... or changes to artifacts */
config . updatedArtifacts ? . length > 0 ) &&
2020-02-04 05:59:13 +00:00
global . trustLevel === 'high' &&
is . nonEmptyArray ( config . allowedPostUpgradeCommands )
) {
logger . debug (
{
tasks : config.postUpgradeTasks ,
allowedCommands : config.allowedPostUpgradeCommands ,
} ,
'Checking for post-upgrade tasks'
) ;
const commands = config . postUpgradeTasks . commands || [ ] ;
const fileFilters = config . postUpgradeTasks . fileFilters || [ ] ;
if ( is . nonEmptyArray ( commands ) ) {
2020-04-22 11:18:15 +00:00
// Persist updated files in file system so any executed commands can see them
for ( const file of concat (
config . updatedPackageFiles ,
config . updatedArtifacts
) ) {
if ( file . name !== '|delete|' ) {
let contents ;
if ( typeof file . contents === 'string' ) {
contents = Buffer . from ( file . contents ) ;
} else {
contents = file . contents ;
}
2020-06-01 05:30:24 +00:00
await writeLocalFile ( file . name , contents ) ;
2020-04-22 11:18:15 +00:00
}
}
2020-02-04 05:59:13 +00:00
for ( const cmd of commands ) {
if (
2020-04-12 16:09:36 +00:00
! config . allowedPostUpgradeCommands . some ( ( pattern ) = >
2020-02-05 18:17:20 +00:00
regEx ( pattern ) . test ( cmd )
2020-02-04 05:59:13 +00:00
)
) {
logger . warn (
{
cmd ,
allowedPostUpgradeCommands : config.allowedPostUpgradeCommands ,
} ,
'Post-upgrade task did not match any on allowed list'
) ;
} else {
logger . debug ( { cmd } , 'Executing post-upgrade task' ) ;
const execResult = await exec ( cmd , {
cwd : config.localDir ,
} ) ;
logger . debug ( { cmd , . . . execResult } , 'Executed post-upgrade task' ) ;
}
}
const status = await platform . getRepoStatus ( ) ;
for ( const relativePath of status . modified . concat ( status . not_added ) ) {
for ( const pattern of fileFilters ) {
if ( minimatch ( relativePath , pattern ) ) {
logger . debug (
{ file : relativePath , pattern } ,
'Post-upgrade file saved'
) ;
2020-04-09 05:43:47 +00:00
const existingContent = await readLocalFile ( relativePath ) ;
2020-02-04 05:59:13 +00:00
config . updatedArtifacts . push ( {
name : relativePath ,
2020-04-09 05:43:47 +00:00
contents : existingContent ,
2020-02-04 05:59:13 +00:00
} ) ;
}
}
}
for ( const relativePath of status . deleted || [ ] ) {
for ( const pattern of fileFilters ) {
if ( minimatch ( relativePath , pattern ) ) {
logger . debug (
{ file : relativePath , pattern } ,
'Post-upgrade file removed'
) ;
config . updatedArtifacts . push ( {
name : '|delete|' ,
contents : relativePath ,
} ) ;
}
}
}
}
}
2019-02-08 13:31:30 +00:00
if ( config . artifactErrors && config . artifactErrors . length ) {
2018-07-14 05:50:37 +00:00
if ( config . releaseTimestamp ) {
logger . debug ( ` Branch timestamp: ` + config . releaseTimestamp ) ;
const releaseTimestamp = DateTime . fromISO ( config . releaseTimestamp ) ;
if ( releaseTimestamp . plus ( { days : 1 } ) < DateTime . local ( ) ) {
2020-02-24 07:43:01 +00:00
logger . debug (
'PR is older than a day, raise PR with lock file errors'
) ;
2018-07-14 05:50:37 +00:00
} else if ( branchExists ) {
2020-02-24 07:43:01 +00:00
logger . debug (
2018-07-14 05:50:37 +00:00
'PR is less than a day old but branchExists so updating anyway'
) ;
} else {
2020-02-24 07:43:01 +00:00
logger . debug ( 'PR is less than a day old - raise error instead of PR' ) ;
2020-01-12 07:50:11 +00:00
throw new Error ( MANAGER_LOCKFILE_ERROR ) ;
2018-07-14 05:50:37 +00:00
}
} else {
logger . debug ( 'PR has no releaseTimestamp' ) ;
}
}
2020-06-02 15:45:24 +00:00
config . forceCommit =
! ! masterIssueCheck || config . rebaseRequested || branchPr ? . isConflicted ;
2020-03-10 10:24:38 +00:00
const commitHash = await commitFilesToBranch ( config ) ;
if ( ! commitHash && ! branchExists ) {
2017-11-27 07:25:07 +00:00
return 'no-work' ;
}
2020-03-10 10:24:38 +00:00
if ( commitHash ) {
2020-02-24 04:33:51 +00:00
const action = branchExists ? 'updated' : 'created' ;
2020-03-10 10:24:38 +00:00
logger . info ( { commitHash } , ` Branch ${ action } ` ) ;
2020-02-24 04:33:51 +00:00
}
2017-08-26 14:10:18 +00:00
// Set branch statuses
2019-08-26 08:32:59 +00:00
await setStability ( config ) ;
2017-08-26 14:10:18 +00:00
await setUnpublishable ( config ) ;
2020-03-03 10:52:18 +00:00
// break if we pushed a new commit because status check are pretty sure pending but maybe not reported yet
if (
2020-04-07 11:39:07 +00:00
! masterIssueCheck &&
2020-03-10 10:24:38 +00:00
commitHash &&
2020-03-03 10:52:18 +00:00
( config . requiredStatusChecks ? . length || config . prCreation !== 'immediate' )
) {
2020-03-10 10:24:38 +00:00
logger . debug ( { commitHash } , ` Branch status pending ` ) ;
2020-03-03 10:52:18 +00:00
return 'pending' ;
}
2018-07-02 07:33:45 +00:00
// Try to automerge branch and finish if successful, but only if branch already existed before this run
2020-03-03 10:52:18 +00:00
if ( branchExists || ! config . requiredStatusChecks ) {
2018-07-02 07:33:45 +00:00
const mergeStatus = await tryBranchAutomerge ( config ) ;
logger . debug ( ` mergeStatus= ${ mergeStatus } ` ) ;
if ( mergeStatus === 'automerged' ) {
logger . debug ( 'Branch is automerged - returning' ) ;
return 'automerged' ;
2018-07-09 09:28:33 +00:00
}
if (
2018-07-02 07:33:45 +00:00
mergeStatus === 'automerge aborted - PR exists' ||
mergeStatus === 'branch status error' ||
mergeStatus === 'failed'
) {
2020-02-24 07:43:01 +00:00
logger . debug ( { mergeStatus } , 'Branch automerge not possible' ) ;
2018-07-02 07:33:45 +00:00
config . forcePr = true ;
config . branchAutomergeFailureMessage = mergeStatus ;
}
2017-08-26 14:10:18 +00:00
}
2018-03-22 08:26:20 +00:00
} catch ( err ) /* istanbul ignore next */ {
2019-03-16 06:32:36 +00:00
if ( err . statusCode === 404 ) {
2020-01-12 07:50:11 +00:00
throw new Error ( REPOSITORY_CHANGED ) ;
2019-03-16 06:32:36 +00:00
}
2020-01-12 07:50:11 +00:00
if ( err . message === PLATFORM_RATE_LIMIT_EXCEEDED ) {
2018-03-22 08:26:20 +00:00
logger . debug ( 'Passing rate-limit-exceeded error up' ) ;
throw err ;
}
2020-01-12 07:50:11 +00:00
if ( err . message === REPOSITORY_CHANGED ) {
2018-02-03 14:45:43 +00:00
logger . debug ( 'Passing repository-changed error up' ) ;
throw err ;
}
2019-02-13 13:47:37 +00:00
if (
err . message &&
err . message . startsWith ( 'remote: Invalid username or password' )
) {
logger . debug ( 'Throwing bad credentials' ) ;
2020-01-12 07:50:11 +00:00
throw new Error ( PLATFORM_BAD_CREDENTIALS ) ;
2019-02-13 13:47:37 +00:00
}
2019-02-13 14:06:51 +00:00
if (
err . message &&
err . message . startsWith (
'ssh_exchange_identification: Connection closed by remote host'
)
) {
logger . debug ( 'Throwing bad credentials' ) ;
2020-01-12 07:50:11 +00:00
throw new Error ( PLATFORM_BAD_CREDENTIALS ) ;
2019-02-13 14:06:51 +00:00
}
2020-01-12 07:50:11 +00:00
if ( err . message === PLATFORM_BAD_CREDENTIALS ) {
2018-05-16 05:03:27 +00:00
logger . debug ( 'Passing bad-credentials error up' ) ;
throw err ;
}
2020-01-12 07:50:11 +00:00
if ( err . message === PLATFORM_INTEGRATION_UNAUTHORIZED ) {
2018-12-03 11:03:46 +00:00
logger . debug ( 'Passing integration-unauthorized error up' ) ;
throw err ;
}
2020-01-12 07:50:11 +00:00
if ( err . message === MANAGER_LOCKFILE_ERROR ) {
2018-07-14 05:50:37 +00:00
logger . debug ( 'Passing lockfile-error up' ) ;
2018-05-24 14:28:36 +00:00
throw err ;
}
2019-03-16 06:33:59 +00:00
if ( err . message && err . message . includes ( 'space left on device' ) ) {
2020-01-12 07:50:11 +00:00
throw new Error ( SYSTEM_INSUFFICIENT_DISK_SPACE ) ;
2019-02-27 08:01:20 +00:00
}
2020-01-12 07:50:11 +00:00
if ( err . message === SYSTEM_INSUFFICIENT_DISK_SPACE ) {
2018-09-08 05:16:05 +00:00
logger . debug ( 'Passing disk-space error up' ) ;
throw err ;
}
2018-10-30 06:40:09 +00:00
if ( err . message . startsWith ( 'Resource not accessible by integration' ) ) {
logger . debug ( 'Passing 403 error up' ) ;
throw err ;
}
2020-01-12 07:50:11 +00:00
if ( err . message === WORKER_FILE_UPDATE_FAILED ) {
2018-10-09 18:03:37 +00:00
logger . warn ( 'Error updating branch: update failure' ) ;
2019-12-21 13:09:00 +00:00
} else if ( err . message . startsWith ( 'bundler-' ) ) {
// we have already warned inside the bundler artifacts error handling, so just return
2019-01-26 14:33:12 +00:00
return 'error' ;
2019-02-02 01:46:32 +00:00
} else if (
err . messagee &&
err . message . includes ( 'fatal: Authentication failed' )
) {
2020-01-12 07:50:11 +00:00
throw new Error ( PLATFORM_AUTHENTICATION_ERROR ) ;
2018-10-09 18:03:37 +00:00
} else if (
2020-01-12 07:50:11 +00:00
err . message !== DATASOURCE_FAILURE &&
2020-01-21 15:23:18 +00:00
err . message !== PLATFORM_FAILURE
2018-07-21 06:38:13 +00:00
) {
2018-03-21 10:17:54 +00:00
logger . error ( { err } , ` Error updating branch: ${ err . message } ` ) ;
}
2017-08-26 14:47:21 +00:00
// Don't throw here - we don't want to stop the other renovations
2017-08-28 13:50:11 +00:00
return 'error' ;
2017-06-29 17:50:26 +00:00
}
2017-06-22 07:03:36 +00:00
try {
2017-08-26 14:10:18 +00:00
logger . debug ( 'Ensuring PR' ) ;
2017-08-27 11:55:41 +00:00
logger . debug (
2019-06-07 04:34:57 +00:00
` There are ${ config . errors . length } errors and ${ config . warnings . length } warnings `
2017-08-27 11:55:41 +00:00
) ;
2020-03-10 09:02:02 +00:00
const { prResult : result , pr } = await ensurePr ( config ) ;
2017-08-26 14:10:18 +00:00
// TODO: ensurePr should check for automerge itself
2020-03-10 08:16:11 +00:00
if ( result === PrResult . AwaitingApproval ) {
2019-07-11 11:48:41 +00:00
return 'needs-pr-approval' ;
}
2020-03-10 08:16:11 +00:00
if (
result === PrResult . AwaitingGreenBranch ||
result === PrResult . AwaitingNotPending
) {
2019-12-13 15:03:00 +00:00
return 'pending' ;
}
2017-08-26 14:10:18 +00:00
if ( pr ) {
2019-09-25 09:40:16 +00:00
const topic = emojify ( ':warning: Artifact update problem' ) ;
2019-02-08 13:31:30 +00:00
if ( config . artifactErrors && config . artifactErrors . length ) {
2017-10-19 12:05:10 +00:00
logger . warn (
2019-02-08 13:31:30 +00:00
{ artifactErrors : config.artifactErrors } ,
'artifactErrors'
2017-10-19 12:05:10 +00:00
) ;
2019-12-05 10:55:14 +00:00
let content = ` Renovate failed to update ` ;
2017-10-19 12:05:10 +00:00
content +=
2019-02-08 13:31:30 +00:00
config . artifactErrors . length > 1 ? 'artifacts' : 'an artifact' ;
content +=
' related to this branch. You probably do not want to merge this PR as-is.' ;
2019-09-25 09:40:16 +00:00
content += emojify (
2019-12-05 10:55:14 +00:00
` \ n \ n:recycle: Renovate will retry this branch, including artifacts, only when one of the following happens: \ n \ n `
2019-09-25 09:40:16 +00:00
) ;
2017-10-19 12:05:10 +00:00
content +=
2018-10-01 16:15:06 +00:00
' - any of the package files in this branch needs updating, or \n' ;
content += ' - the branch becomes conflicted, or\n' ;
2019-02-08 13:31:30 +00:00
content +=
' - you check the rebase/retry checkbox if found above, or\n' ;
2018-10-01 16:15:06 +00:00
content +=
' - you rename this PR\'s title to start with "rebase!" to trigger it manually' ;
2019-09-07 12:51:00 +00:00
content += '\n\nThe artifact failure details are included below:\n\n' ;
2020-04-12 16:09:36 +00:00
config . artifactErrors . forEach ( ( error ) = > {
2019-09-07 12:51:00 +00:00
content += ` ##### File name: ${ error . lockFile } \ n \ n ` ;
content += ` \` \` \` \ n ${ error . stderr } \ n \` \` \` \ n \ n ` ;
} ) ;
2019-02-08 13:31:30 +00:00
if (
! (
config . suppressNotifications . includes ( 'artifactErrors' ) ||
config . suppressNotifications . includes ( 'lockFileErrors' )
)
) {
2018-12-08 11:42:56 +00:00
if ( config . dryRun ) {
logger . info (
'DRY-RUN: Would ensure lock file error comment in PR #' +
pr . number
) ;
} else {
2020-01-14 11:13:35 +00:00
await platform . ensureComment ( {
number : pr . number ,
topic ,
content ,
} ) ;
2018-12-08 11:42:56 +00:00
}
2018-10-26 07:48:49 +00:00
}
2019-12-05 09:45:28 +00:00
const context = ` renovate/artifacts ` ;
2019-02-08 13:31:30 +00:00
const description = 'Artifact file update failure' ;
2020-03-08 14:03:19 +00:00
const state = BranchStatus . red ;
2018-07-14 05:50:37 +00:00
const existingState = await platform . getBranchStatusCheck (
config . branchName ,
context
) ;
// Check if state needs setting
if ( existingState !== state ) {
logger . debug ( ` Updating status check state to failed ` ) ;
2018-10-26 07:48:49 +00:00
if ( config . dryRun ) {
logger . info (
'DRY-RUN: Would set branch status in ' + config . branchName
) ;
} else {
2020-01-07 09:59:14 +00:00
await platform . setBranchStatus ( {
branchName : config.branchName ,
2018-10-26 07:48:49 +00:00
context ,
description ,
2020-01-07 09:59:14 +00:00
state ,
} ) ;
2018-10-26 07:48:49 +00:00
}
2018-07-14 05:50:37 +00:00
}
2017-10-19 12:05:10 +00:00
} else {
2019-02-08 13:31:30 +00:00
if ( config . updatedArtifacts && config . updatedArtifacts . length ) {
2018-10-26 07:48:49 +00:00
// istanbul ignore if
if ( config . dryRun ) {
logger . info (
'DRY-RUN: Would ensure comment removal in PR #' + pr . number
) ;
} else {
2020-06-09 11:42:22 +00:00
// Remove artifacts error comment only if this run has successfully updated artifacts
2020-05-03 19:03:55 +00:00
await platform . ensureCommentRemoval ( { number : pr . number , topic } ) ;
2018-10-26 07:48:49 +00:00
}
2017-11-16 20:54:13 +00:00
}
2019-10-18 12:30:51 +00:00
const prAutomerged = await checkAutoMerge ( pr , config ) ;
2017-10-19 12:05:10 +00:00
if ( prAutomerged ) {
return 'automerged' ;
}
2017-08-28 13:50:11 +00:00
}
2017-06-22 07:03:36 +00:00
}
2018-03-23 06:56:21 +00:00
} catch ( err ) /* istanbul ignore next */ {
2018-12-03 11:04:04 +00:00
if (
[
2020-01-12 07:50:11 +00:00
PLATFORM_RATE_LIMIT_EXCEEDED ,
PLATFORM_FAILURE ,
REPOSITORY_CHANGED ,
2018-12-03 11:04:04 +00:00
] . includes ( err . message )
) {
2018-07-23 14:47:37 +00:00
logger . debug ( 'Passing PR error up' ) ;
2018-06-19 13:34:37 +00:00
throw err ;
}
2018-03-23 06:56:21 +00:00
// Otherwise don't throw here - we don't want to stop the other renovations
2018-03-27 15:44:05 +00:00
logger . error ( { err } , ` Error ensuring PR: ${ err . message } ` ) ;
2017-06-22 07:03:36 +00:00
}
2018-01-11 10:49:01 +00:00
if ( ! branchExists ) {
return 'pr-created' ;
}
2017-08-28 13:50:11 +00:00
return 'done' ;
2017-06-22 07:03:36 +00:00
}