зеркало из https://github.com/github/docs.git
146 строки
3.8 KiB
JavaScript
Executable File
146 строки
3.8 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
// [start-readme]
|
|
//
|
|
// This script removes all stale Heroku staging apps that outlasted the closure
|
|
// of their corresponding pull requests, or correspond to spammy pull requests.
|
|
//
|
|
// [end-readme]
|
|
|
|
import dotenv from 'dotenv'
|
|
import { chain } from 'lodash-es'
|
|
import chalk from 'chalk'
|
|
import Heroku from 'heroku-client'
|
|
import getOctokit from './helpers/github.js'
|
|
|
|
dotenv.config()
|
|
|
|
// Check for required Heroku API token
|
|
if (!process.env.HEROKU_API_TOKEN) {
|
|
console.error(
|
|
'Error! You must have a HEROKU_API_TOKEN environment variable for deployer-level access.'
|
|
)
|
|
process.exit(1)
|
|
}
|
|
// Check for required GitHub PAT
|
|
if (!process.env.GITHUB_TOKEN) {
|
|
console.error('Error! You must have a GITHUB_TOKEN environment variable for repo access.')
|
|
process.exit(1)
|
|
}
|
|
|
|
const heroku = new Heroku({ token: process.env.HEROKU_API_TOKEN })
|
|
// This helper uses the `GITHUB_TOKEN` implicitly
|
|
const octokit = getOctokit()
|
|
|
|
const protectedAppNames = ['help-docs', 'help-docs-deployer']
|
|
|
|
main()
|
|
|
|
async function main() {
|
|
const apps = chain(await heroku.get('/apps'))
|
|
.orderBy('name')
|
|
.value()
|
|
|
|
const prInfoMatch = /^(?:gha-|ghd-)?(?<repo>docs(?:-internal)?)-(?<pullNumber>\d+)--.*$/
|
|
|
|
const appsPlusPullIds = apps.map((app) => {
|
|
const match = prInfoMatch.exec(app.name)
|
|
const { repo, pullNumber } = (match || {}).groups || {}
|
|
|
|
return {
|
|
app,
|
|
repo,
|
|
pullNumber: parseInt(pullNumber, 10) || null,
|
|
}
|
|
})
|
|
|
|
const appsWithPullIds = appsPlusPullIds.filter((appi) => appi.repo && appi.pullNumber > 0)
|
|
|
|
const nonMatchingAppNames = appsPlusPullIds
|
|
.filter((appi) => !(appi.repo && appi.pullNumber > 0))
|
|
.map((appi) => appi.app.name)
|
|
.filter((name) => !protectedAppNames.includes(name))
|
|
|
|
let staleCount = 0
|
|
let spammyCount = 0
|
|
for (const awpi of appsWithPullIds) {
|
|
const { isStale, isSpammy } = await assessPullRequest(awpi.repo, awpi.pullNumber)
|
|
|
|
if (isSpammy) spammyCount++
|
|
if (isStale) staleCount++
|
|
|
|
if (isSpammy || isStale) {
|
|
await deleteHerokuApp(awpi.app.name)
|
|
}
|
|
}
|
|
|
|
const matchingCount = appsWithPullIds.length
|
|
const counts = {
|
|
total: matchingCount,
|
|
alive: matchingCount - staleCount,
|
|
stale: {
|
|
total: staleCount,
|
|
spammy: spammyCount,
|
|
closed: staleCount - spammyCount,
|
|
},
|
|
}
|
|
console.log(`🧮 COUNTS!\n${JSON.stringify(counts, null, 2)}`)
|
|
|
|
const nonMatchingCount = nonMatchingAppNames.length
|
|
if (nonMatchingCount > 0) {
|
|
console.log(
|
|
'⚠️ 👀',
|
|
chalk.yellow(
|
|
`Non-matching app names (${nonMatchingCount}):\n - ${nonMatchingAppNames.join('\n - ')}`
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
function displayParams(params) {
|
|
const { owner, repo, pull_number: pullNumber } = params
|
|
return `${owner}/${repo}#${pullNumber}`
|
|
}
|
|
|
|
async function assessPullRequest(repo, pullNumber) {
|
|
const params = {
|
|
owner: 'github',
|
|
repo: repo,
|
|
pull_number: pullNumber,
|
|
}
|
|
|
|
let isStale = false
|
|
let isSpammy = false
|
|
try {
|
|
const { data: pullRequest } = await octokit.pulls.get(params)
|
|
|
|
if (pullRequest && pullRequest.state === 'closed') {
|
|
isStale = true
|
|
console.debug(chalk.green(`STALE: ${displayParams(params)} is closed`))
|
|
}
|
|
} catch (error) {
|
|
// Using a standard GitHub PAT, PRs from spammy users will respond as 404
|
|
if (error.status === 404) {
|
|
isStale = true
|
|
isSpammy = true
|
|
console.debug(chalk.yellow(`STALE: ${displayParams(params)} is spammy or deleted`))
|
|
} else {
|
|
console.debug(chalk.red(`ERROR: ${displayParams(params)} - ${error.message}`))
|
|
}
|
|
}
|
|
|
|
return { isStale, isSpammy }
|
|
}
|
|
|
|
async function deleteHerokuApp(appName) {
|
|
try {
|
|
await heroku.delete(`/apps/${appName}`)
|
|
console.log('✅', chalk.green(`Removed stale app "${appName}"`))
|
|
} catch (error) {
|
|
console.log(
|
|
'❌',
|
|
chalk.red(`ERROR: Failed to remove stale app "${appName}" - ${error.message}`)
|
|
)
|
|
}
|
|
}
|