Files
cypress/scripts/wait-on-circle-jobs.js
T
Emily Rohrbough 40092c0fe6 chore(deps): bump decode-uri-component and ejs in system-tests projects (#26289)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Emily Rohrbough <emilyrohrbough@users.noreply.github.com>
2023-03-30 15:38:42 -05:00

157 lines
4.6 KiB
JavaScript

const _ = require('lodash')
const minimist = require('minimist')
const Promise = require('bluebird')
const retry = require('bluebird-retry')
const got = require('got')
// always print the debug logs
const debug = require('debug')('*')
const { seconds, minutes } = require('./utils')
// we expect CircleCI to set the current polling job name
const jobName = process.env.CIRCLE_JOB || 'wait-on-circle-jobs'
const workflowId = process.env.CIRCLE_WORKFLOW_ID
const getAuth = () => `${process.env.CIRCLE_TOKEN}:`
const verifyCI = () => {
if (!process.env.CIRCLE_TOKEN) {
console.error('Cannot find CIRCLE_TOKEN')
process.exit(1)
}
if (!process.env.CIRCLE_WORKFLOW_ID) {
console.error('Cannot find CIRCLE_WORKFLOW_ID')
process.exit(1)
}
}
/**
* Job status
* - blocked (has not run yet)
* - running (currently running)
* - failed | success
*/
const getJobStatus = async (workflowId) => {
const auth = getAuth()
// typo at https://circleci.com/docs/2.0/api-intro/
// to retrieve all jobs, the url is "/workflow/:id/job"
const url = `https://${auth}@circleci.com/api/v2/workflow/${workflowId}/job`
const response = await got(url).json()
// returns something like
// {
// next_page_token: null,
// items: [
// {
// dependencies: [],
// job_number: 400959,
// id: '7021bcc7-90c1-47d9-bf99-c0372a4f8f49',
// started_at: '2020-07-20T19:45:46Z',
// name: 'build',
// project_slug: 'gh/cypress-io/cypress',
// status: 'success',
// type: 'build',
// stopped_at: '2020-07-20T19:50:07Z'
// }
// ]
// }
return response
}
const waitForAllJobs = async (jobNames, workflowId) => {
let response
try {
response = await getJobStatus(workflowId)
} catch (e) {
console.error(e)
process.exit(1)
}
// if a job is pending, its status will be "blocked"
const blockedJobs = _.filter(response.items, { status: 'blocked' })
const failedJobs = _.filter(response.items, { status: 'failed' })
const runningJobs = _.filter(response.items, { status: 'running' })
const successfulJobNames = _.filter(response.items, { status: 'success' }).map((w) => w.name)
const blockedJobNames = _.map(blockedJobs, 'name')
const runningJobNames = _.map(runningJobs, 'name')
const failedJobNames = _.map(failedJobs, 'name')
debug('failed jobs %o', _.map(failedJobs, 'name'))
debug('blocked jobs %o', blockedJobNames)
debug('running jobs %o', runningJobNames)
if (_.intersection(jobNames, failedJobNames).length) {
console.error('At least one failing job has prevented percy-finalize from running', failedJobs)
process.exit(1)
}
const waitOnJobsOnlyJobLeft = !runningJobs.length || (runningJobs.length === 1 && runningJobs[0].name === jobName)
const allWaitForJobsWereSuccessful = _.intersection(jobNames, successfulJobNames).length === jobNames.length
if (waitOnJobsOnlyJobLeft && allWaitForJobsWereSuccessful) {
// there are no more jobs to run, or this is the last running job
console.log('all jobs are done, finishing this job')
return Promise.resolve()
}
const futureOrRunning = _.union(blockedJobs, runningJobNames)
const jobsToWaitFor = _.intersection(jobNames, futureOrRunning)
// logging something every time this runs will avoid CI timing out if there is no activity for 10 mins.
console.log(`waiting for ${jobsToWaitFor.length} jobs to finish, jobs outstanding:\n - `, jobsToWaitFor.join('\n - '))
debug('jobs to wait for %o', jobsToWaitFor)
if (!jobsToWaitFor.length && allWaitForJobsWereSuccessful) {
console.log('No more jobs to wait for and all jobs were successful!')
return Promise.resolve()
}
return Promise.reject(new Error('Jobs have not finished'))
}
const main = () => {
verifyCI()
const args = minimist(process.argv.slice(2), { boolean: false })
const jobNames = _
.chain(args['job-names'])
.split(',')
.without('true')
.map(_.trim)
.compact()
.value()
if (!jobNames.length) {
console.error('Missing argument: --job-names')
console.error('You must pass a comma separated list of Circle CI job names to wait for.')
process.exit(1)
}
debug('received circle jobs: %o', jobNames)
// https://github.com/demmer/bluebird-retry
retry(waitForAllJobs.bind(null, jobNames, workflowId), {
timeout: minutes(30), // max time for this job
interval: seconds(30), // poll intervals
max_interval: seconds(30),
}).then(() => {
console.log('all done')
}, (err) => {
console.error(err)
process.exit(1)
})
}
// execute main function if called from command line
if (require.main === module) {
main()
}