mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-22 23:20:24 -05:00
40092c0fe6
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Emily Rohrbough <emilyrohrbough@users.noreply.github.com>
157 lines
4.6 KiB
JavaScript
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()
|
|
}
|