Remove unused things, mostly Azure-related (#54192)
Co-authored-by: Kevin Heis <heiskr@users.noreply.github.com> Co-authored-by: Evan Bonsignori <ebonsignori@github.com>
This commit is contained in:
3
.github/workflows/codeql.yml
vendored
3
.github/workflows/codeql.yml
vendored
@@ -39,9 +39,6 @@ jobs:
|
||||
- uses: github/codeql-action/init@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0
|
||||
with:
|
||||
languages: javascript # comma separated list of values from {go, python, javascript, java, cpp, csharp, ruby}
|
||||
config: |
|
||||
paths-ignore:
|
||||
- 'src/open-source/scripts/add-pr-links.js'
|
||||
- uses: github/codeql-action/analyze@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0
|
||||
continue-on-error: true
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
data/release-notes/
|
||||
src/bookmarklets/
|
||||
src/open-source/scripts/add-pr-links.js
|
||||
/.next/
|
||||
|
||||
/.coverage
|
||||
|
||||
@@ -232,8 +232,6 @@
|
||||
"!/.*",
|
||||
"/.next/",
|
||||
"src/bookmarklets/*",
|
||||
"src/open-source/scripts/add-pr-links.js",
|
||||
"src/open-source/scripts/pr-link-source.js",
|
||||
"rest-api-description/",
|
||||
"docs-internal-data/",
|
||||
"src/code-scanning/scripts/generate-code-scanning-query-list.ts"
|
||||
|
||||
@@ -39,11 +39,3 @@ The installation requires a few steps:
|
||||
1. Paste the path in place of where it says `REPLACE_ME` in line 1 (make sure to leave the single quotes around it).
|
||||
1. Change the title to something like `Open in VSC`.
|
||||
1. Drag the generated link onto your bookmarks bar.
|
||||
|
||||
## Add preview links to PRs
|
||||
|
||||
[`src/bookmarklets/add-pr-links.js`](./add-pr-links.js)
|
||||
|
||||
This bookmarklet modifies the `Files changed` page of a GitHub pull request that has a current staging deployment. For each Markdown file in the diff view, it adds links to the preview deployment of the file for each version: `FPT / GHEC / GHES / AE`. (Some of these may redirect to another version or 404 if that version of the page doesn't exist.)
|
||||
|
||||
Note: readable JavaScript source lives in `src/bookmarklets/pr-link-source.js`. The bookmarklet code was generated via https://chriszarate.github.io/bookmarkleter.
|
||||
|
||||
@@ -5,7 +5,7 @@ const router = express.Router()
|
||||
|
||||
/**
|
||||
* Returns the healthiness of the service.
|
||||
* This may be used by azure app service (forthcoming) to determine whether this
|
||||
* This may be used by Moda to determine whether this
|
||||
* instance remains in the pool to handle requests
|
||||
* For example: if we have a failing database connection we may return a 500 status here.
|
||||
*/
|
||||
|
||||
@@ -115,9 +115,8 @@ export default function (app: Express) {
|
||||
app.use(datadog)
|
||||
}
|
||||
|
||||
// Put this early to make it as fast as possible because it's used,
|
||||
// and used very often, by the Azure load balancer to check the
|
||||
// health of each node.
|
||||
// Put this early to make it as fast as possible because it's used
|
||||
// to check the health of each cluster.
|
||||
app.use('/healthcheck', healthcheck)
|
||||
|
||||
// Must appear before static assets and all other requests
|
||||
|
||||
@@ -30,10 +30,10 @@ describe('robots.txt', () => {
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
test('disallows indexing of azurecontainer.io domains', async () => {
|
||||
test('disallows indexing of internal domains', async () => {
|
||||
const res = await get('/robots.txt', {
|
||||
headers: {
|
||||
host: 'docs-internal-preview-12345-asdfz.azurecontainer.io',
|
||||
host: 'docs-internal.github.com',
|
||||
},
|
||||
})
|
||||
expect(res.body).toEqual('User-agent: *\nDisallow: /')
|
||||
|
||||
@@ -26,7 +26,7 @@ Periodically, translators read the `content/**` and `data/**` directories from `
|
||||
|
||||
## Deployment of translated content
|
||||
|
||||
In the deployment workflow, we [checkout](https://github.com/github/docs-internal/blob/a8e52aad1a6b67f41da92d314bd7fd8cd84193a4/.github/workflows/azure-prod-build-deploy.yml#L90-L92) each and every translation repo and put their contents into the `translations/` directory.
|
||||
During the build step of our deployment, we checkout every translation repo into the `translations/` directory.
|
||||
|
||||
The enabled languages and their source directories are interpreted in [`src/languages/lib/languages.js`](https://github.com/github/docs-internal/blob/a8e52aad1a6b67f41da92d314bd7fd8cd84193a4/src/languages/lib/languages.js), which ensures English and translated content are in the same Docker image we deploy.
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import walk from 'walk-sync'
|
||||
import frontmatter from 'src/frame/lib/read-frontmatter.js'
|
||||
import { loadPages } from 'src/frame/lib/page-data.js'
|
||||
import patterns from 'src/frame/lib/patterns.js'
|
||||
import loadRedirects from 'src/redirects/lib/precompile.js'
|
||||
import { allVersionKeys } from 'src/versions/lib/all-versions.js'
|
||||
|
||||
// get all content and data files
|
||||
const files = ['content', 'data']
|
||||
.map((dir) => {
|
||||
return walk(path.join(process.cwd(), dir), {
|
||||
includeBasePath: true,
|
||||
directories: false,
|
||||
}).filter((file) => file.endsWith('.md') && !file.endsWith('README.md'))
|
||||
})
|
||||
.flat()
|
||||
|
||||
// match [foo](/v3) and [bar](/v4) Markdown links
|
||||
const linkRegex = /\(\/v[34].*?\)/g
|
||||
|
||||
main()
|
||||
|
||||
async function main() {
|
||||
// we need to load the pages so we can get the redirects
|
||||
const englishPages = (await loadPages()).filter((p) => p.languageCode === 'en')
|
||||
const redirects = await loadRedirects(englishPages)
|
||||
|
||||
for (const file of files) {
|
||||
const { data, content } = frontmatter(fs.readFileSync(file, 'utf8'))
|
||||
|
||||
const links = content?.match(linkRegex)
|
||||
if (!links) continue
|
||||
|
||||
// remove parentheses: (/v3) -> /v3
|
||||
// also remove trailing slash before closing parens if there is one
|
||||
const devLinks = links.map((link) => link.replace('(', '').replace(/\/?\)/, ''))
|
||||
|
||||
let newContent = content
|
||||
|
||||
for (const devLink of devLinks) {
|
||||
const [link, fragment] = devLink.split(/\/?#/)
|
||||
|
||||
let redirect = redirects[link]
|
||||
|
||||
if (!redirect) {
|
||||
console.log(`no redirect found for ${devLink} in ${file}`)
|
||||
continue
|
||||
}
|
||||
|
||||
// do some cleanup
|
||||
redirect = redirect
|
||||
// remove language code segment
|
||||
.replace(patterns.getLanguageCode, '')
|
||||
// remove version segment
|
||||
.replace(new RegExp(`/(${allVersionKeys.join('|')})`), '')
|
||||
|
||||
// re-add the fragment after removing any fragment added via the redirect
|
||||
// otherwise /v3/git/refs/#create-a-reference will become /rest/reference/git#refs#create-a-reference
|
||||
// we want to preserve the #create-a-reference fragment, not #refs
|
||||
const newLink = fragment ? redirect.replace(/#.+?$/, '') + '#' + fragment : redirect
|
||||
|
||||
// first replace the old link with the new link
|
||||
// then remove any trailing slashes
|
||||
newContent = newContent?.replace(new RegExp(`${devLink}/?(?=\\))`), newLink)
|
||||
}
|
||||
|
||||
fs.writeFileSync(file, frontmatter.stringify(newContent || '', data || {}))
|
||||
}
|
||||
console.log('Done!')
|
||||
}
|
||||
@@ -24,7 +24,6 @@ export default new StatsD({
|
||||
// DD_AGENT_HOST and DD_DOGSTATSD_PORT environment variables.
|
||||
// If undefined, the host will default to 'localhost' and the port
|
||||
// will default to 8125.
|
||||
// Azure docker templates configure DD_AGENT_HOST but not DD_DOGSTATSD_PORT.
|
||||
// Moda configuration defines DD_DOGSTATSD_PORT but not DD_AGENT_HOST.
|
||||
// For Moda, the host must be set to the Kubernetes node name, which is
|
||||
// set in KUBE_NODE_HOSTNAME.
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Open source
|
||||
|
||||
The open source subject folder contains files that relate to the github/docs repository, so that we can support open source contributors to docs.github.com.
|
||||
@@ -1 +0,0 @@
|
||||
javascript:void%20function(){!async%20function(){function%20a(a,b,c){var%20d=document.createElement(%22A%22);return%20d.innerHTML=b,d.href=c,d.target=%22_blank%22,a.appendChild(d),a}const%20b=/https:\/\/github.com\/github\/([^\/]*)\/pull\/\d*\/files/;if(!window.location.href.match(b))return%20void%20window.alert(%22You're%20not%20on%20a%20PR%20'Files%20changed'%20page.%20\uD83D\uDE43%22);let%20c=window.location.href.replace(/files.*/g,%22%22),d=await%20fetch(c).then(function(a){return%20a.text()}).then(function(a){for(var%20b=new%20DOMParser,c=b.parseFromString(a,%22text/html%22),d=c.getElementsByClassName(%22TimelineItem%22),e=0;e%3Cd.length;e++)for(var%20f,g=d[e],h=g.getElementsByTagName(%22a%22),k=0;k%3Ch.length;k++)if(f=h[k],f.innerText.match(/View%20deployment/))var%20l=f.getAttribute(%22href%22);return%20l});if(null==d)return%20void%20window.alert(%22No%20preview%20deployment%20found!%20\uD83D\uDE2D%22);d=d.replace(/\/$/,%22%22);var%20e=d+%22/en%22,f=d+%22/en/enterprise-cloud%40latest%22,g=d+%22/en/enterprise-server%40latest%22,h=d+%22/en/github-ae%40latest%22;const%20j=document.querySelectorAll(%22div.file-info%22);for(var%20k,l=0;l%3Cj.length;l++)if(k=j[l].querySelector(%22a%22).title,0===k.search(%22data/%22))continue;else{var%20m=/\.md$/,n=0%3C=k.search(m);if(n){console.log(%22link:%20%22+k),k=k.replace(m,%22%22),k=k.replace(/^content/,%22%22),k=k.replace(/\/index/,%22%22);var%20o=document.createElement(%22SPAN%22);o.style.fontFamily=%22-apple-system,BlinkMacSystemFont,Segoe%20UI,Helvetica,Arial,sans-serif%22,o.innerHTML=%22%26nbsp;%20View:%20%22,o=a(o,%22FPT%22,e+k),o.innerHTML+=%22%20/%20%22,o=a(o,%22GHEC%22,f+k),o.innerHTML+=%22%20/%20%22,o=a(o,%22GHES%22,g+k),o.innerHTML+=%22%20/%20%22,o=a(o,%22AE%22,h+k),j[l].appendChild(o)}}}()}();
|
||||
@@ -1,87 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// [start-readme]
|
||||
//
|
||||
// In the .github/workflows, We use...
|
||||
//
|
||||
// uses: some/action@95cb08cb2672c73d4ffd2f422e6d11953d2a9c70
|
||||
//
|
||||
// But sometimes we fail to update the uniformly. This script
|
||||
// is for finding these unicorns.
|
||||
//
|
||||
// [end-readme]
|
||||
//
|
||||
//
|
||||
|
||||
import fs from 'fs'
|
||||
|
||||
import { program } from 'commander'
|
||||
import walk from 'walk-sync'
|
||||
import chalk from 'chalk'
|
||||
|
||||
program
|
||||
.description('Finds action shas that are unusual')
|
||||
.option('-v, --verbose', 'Verbose outputs')
|
||||
.parse(process.argv)
|
||||
|
||||
main(program.opts(), program.args)
|
||||
|
||||
async function main(opts, args) {
|
||||
const files = walk('.github/workflows', { globs: ['*.yml'], includeBasePath: true })
|
||||
const counts = {}
|
||||
const places = {}
|
||||
|
||||
for (const file of files) {
|
||||
const content = fs.readFileSync(file, 'utf-8')
|
||||
let lineNo = 0
|
||||
for (const line of content.split(/\n/g)) {
|
||||
lineNo++
|
||||
if (line.includes('uses:') && /@[a-f0-9]{40}/.test(line)) {
|
||||
const match = line.match(/\b(?<name>[\w/-]+)@(?<sha>[a-f0-9]{40})/)
|
||||
const whole = match[0]
|
||||
if (!places[whole]) {
|
||||
places[whole] = []
|
||||
}
|
||||
places[whole].push({ file, line, lineNo })
|
||||
const { name, sha } = match.groups
|
||||
if (!counts[name]) {
|
||||
counts[name] = {}
|
||||
}
|
||||
counts[name][sha] = 1 + (counts[name][sha] || 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
const suspects = Object.fromEntries(
|
||||
Object.entries(counts).filter(([, shas]) => Object.keys(shas).length > 1),
|
||||
)
|
||||
|
||||
const countSuspects = Object.keys(suspects).length
|
||||
if (countSuspects) {
|
||||
console.log(chalk.yellow(`Found ${countSuspects} suspect${countSuspects === 1 ? '' : 's'}\n`))
|
||||
|
||||
for (const [action, shas] of Object.entries(suspects)) {
|
||||
const total = Object.values(shas).reduce((a, b) => a + b, 0)
|
||||
const flat = Object.entries(shas)
|
||||
.map(([sha, count]) => [count, sha])
|
||||
.sort((a, b) => b[0] - a[0])
|
||||
|
||||
const mostPopular = flat[0]
|
||||
for (const [count, sha] of flat.slice(1)) {
|
||||
console.log(chalk.bold('Suspect:'), `${action}@${chalk.yellow(sha)}`)
|
||||
console.log(
|
||||
`is only used ${count} time${count === 1 ? '' : 's'} (${((100 * count) / total).toFixed(
|
||||
1,
|
||||
)}%) compared to ${mostPopular[1]} (used ${mostPopular[0]} times)`,
|
||||
)
|
||||
console.log(chalk.bold(`Consider changing to ${action}@${mostPopular[1]}`))
|
||||
console.log('in...')
|
||||
for (const { file, lineNo } of places[`${action}@${sha}`]) {
|
||||
console.log(`\t${file} (line ${lineNo})`)
|
||||
}
|
||||
console.log('\n')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(chalk.green('All good! No suspects found 😎'))
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
!(async function () {
|
||||
const regexp = /https:\/\/github.com\/github\/([^\/]*)\/pull\/\d*\/files/
|
||||
|
||||
if (!window.location.href.match(regexp)) {
|
||||
window.alert("You're not on a PR 'Files changed' page. 🙃")
|
||||
return
|
||||
}
|
||||
|
||||
let conversation_url = window.location.href.replace(/files.*/g, '')
|
||||
|
||||
// get the preview deployment URL by loading the 'Conversation' page, and searching for the 'View deployment' link
|
||||
let deployment_url = await fetch(conversation_url)
|
||||
.then(function (response) {
|
||||
return response.text()
|
||||
})
|
||||
.then(function (html) {
|
||||
// Convert the HTML string into a document object
|
||||
var parser = new DOMParser()
|
||||
var doc = parser.parseFromString(html, 'text/html')
|
||||
|
||||
var elements = doc.getElementsByClassName('TimelineItem')
|
||||
// Find the element that is a link that contains the text "View deployment"
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var element = elements[i]
|
||||
var links = element.getElementsByTagName('a')
|
||||
for (var j = 0; j < links.length; j++) {
|
||||
var link = links[j]
|
||||
if (link.innerText.match(/View deployment/)) {
|
||||
// Get the href of the link
|
||||
var deployment_url = link.getAttribute('href')
|
||||
}
|
||||
}
|
||||
}
|
||||
// This should return the last link that contains the text "View deployment" (there might be multiple ones if there are multiple deployments)
|
||||
return deployment_url
|
||||
})
|
||||
if (deployment_url == null) {
|
||||
window.alert('No preview deployment found! 😭')
|
||||
return
|
||||
}
|
||||
// strip any trailing slash from deployment_url
|
||||
deployment_url = deployment_url.replace(/\/$/, '')
|
||||
|
||||
var url_fpt = deployment_url + '/en'
|
||||
var url_ghec = deployment_url + '/en/enterprise-cloud@latest'
|
||||
var url_ghes = deployment_url + '/en/enterprise-server@latest'
|
||||
var url_ae = deployment_url + '/en/github-ae@latest'
|
||||
var fpt = 'FPT'
|
||||
var ghes = 'GHES'
|
||||
var ghec = 'GHEC'
|
||||
var ae = 'AE'
|
||||
|
||||
const file_info = document.querySelectorAll('div.file-info')
|
||||
for (var i = 0; i < file_info.length; i++) {
|
||||
var link = file_info[i].querySelector('a').title
|
||||
if (link.search('data/') === 0) {
|
||||
continue
|
||||
} else {
|
||||
var regex = /\.md$/
|
||||
var markdownfile = link.search(regex) >= 0
|
||||
if (markdownfile) {
|
||||
console.log('link: ' + link)
|
||||
link = link.replace(regex, '')
|
||||
link = link.replace(/^content/, '')
|
||||
link = link.replace(/\/index/, '')
|
||||
var span = document.createElement('SPAN')
|
||||
span.style.fontFamily =
|
||||
'-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif'
|
||||
span.innerHTML = ' View: '
|
||||
|
||||
span = addLink(span, fpt, url_fpt + link)
|
||||
span.innerHTML += ' / '
|
||||
span = addLink(span, ghec, url_ghec + link)
|
||||
span.innerHTML += ' / '
|
||||
span = addLink(span, ghes, url_ghes + link)
|
||||
span.innerHTML += ' / '
|
||||
span = addLink(span, ae, url_ae + link)
|
||||
|
||||
file_info[i].appendChild(span)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addLink(span, link_name, link_href) {
|
||||
var anchor = document.createElement('A')
|
||||
anchor.innerHTML = link_name
|
||||
anchor.href = link_href
|
||||
anchor.target = '_blank'
|
||||
span.appendChild(anchor)
|
||||
return span
|
||||
}
|
||||
})()
|
||||
@@ -23,13 +23,13 @@ At its root, the `src/shielding/frame/middleware/index.js` is injected into our
|
||||
Express server. From there, it loads all its individual middleware handlers.
|
||||
|
||||
Each middleware is one file that focuses on a single use-case. The
|
||||
use-cases are borne from studying log files (CDN and Azure App Service) to
|
||||
use-cases are borne from studying log files to
|
||||
spot patterns of request abuse.
|
||||
|
||||
## Notes
|
||||
|
||||
- The best place to do shielding is as close to the client(s) as possible,
|
||||
i.e. in the CDN or in Azure Frontdoor. Having the code in our own backend
|
||||
i.e. in the CDN. Having the code in our own backend
|
||||
has the advantage that it's easier to write custom business logic
|
||||
along with end-to-end tests.
|
||||
- Some shielding "tricks" appear in other places throughout the code
|
||||
|
||||
@@ -19,8 +19,8 @@ export function createRateLimiter(max = MAX) {
|
||||
// 1 minute
|
||||
windowMs: EXPIRES_IN_AS_SECONDS * 1000,
|
||||
// limit each IP to X requests per windowMs
|
||||
// We currently have about 25 instances in production. That's routed
|
||||
// in Azure to spread the requests to each healthy instance.
|
||||
// We currently have about 12 instances in production. That's routed
|
||||
// in Moda to spread the requests to each healthy instance.
|
||||
// So, the true rate limit, per `windowMs`, is this number multiplied
|
||||
// by the current number of instances.
|
||||
max: max,
|
||||
@@ -32,7 +32,7 @@ export function createRateLimiter(max = MAX) {
|
||||
|
||||
keyGenerator: (req) => {
|
||||
let { ip } = req
|
||||
// In our Azure preview environment, with the way the proxying works,
|
||||
// In our previous environments, with the way the proxying works,
|
||||
// the `x-forwarded-for` is always the origin IP with a port number
|
||||
// attached. E.g. `75.40.90.27:56675, 169.254.129.1`
|
||||
// This port number portion changes with every request, so we strip it.
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"appName": {
|
||||
"defaultValue": null,
|
||||
"type": "string",
|
||||
"minLength": 5,
|
||||
"maxLength": 63,
|
||||
"metadata": {
|
||||
"description": "A unique name for the app"
|
||||
}
|
||||
},
|
||||
"containerImage": {
|
||||
"type": "string",
|
||||
"defaultValue": null,
|
||||
"metadata": {
|
||||
"description": "Container image to deploy"
|
||||
}
|
||||
},
|
||||
"dockerRegistryUrl": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"description": "Should be a valid host name without protocol"
|
||||
}
|
||||
},
|
||||
"dockerRegistryUsername": {
|
||||
"type": "String"
|
||||
},
|
||||
"dockerRegistryPassword": {
|
||||
"type": "SecureString"
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"type": "Microsoft.ContainerInstance/containerGroups",
|
||||
"name": "[parameters('appName')]",
|
||||
"apiVersion": "2021-07-01",
|
||||
"location": "[resourceGroup().location]",
|
||||
"properties": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "app",
|
||||
"properties": {
|
||||
"image": "[parameters('containerImage')]",
|
||||
"ports": [
|
||||
{
|
||||
"protocol": "TCP",
|
||||
"port": 4000
|
||||
}
|
||||
],
|
||||
"environmentVariables": [
|
||||
{
|
||||
"name": "PORT",
|
||||
"value": "4000"
|
||||
},
|
||||
{
|
||||
"name": "NODE_ENV",
|
||||
"value": "production"
|
||||
},
|
||||
{
|
||||
"name": "WEB_CONCURRENCY",
|
||||
"value": "1"
|
||||
}
|
||||
],
|
||||
"resources": {
|
||||
"requests": {
|
||||
"memoryInGB": 4,
|
||||
"cpu": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"imageRegistryCredentials": [
|
||||
{
|
||||
"server": "[parameters('dockerRegistryUrl')]",
|
||||
"username": "[parameters('dockerRegistryUsername')]",
|
||||
"password": "[parameters('dockerRegistryPassword')]"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"ipAddress": {
|
||||
"ports": [
|
||||
{
|
||||
"protocol": "TCP",
|
||||
"port": 4000
|
||||
}
|
||||
],
|
||||
"type": "Public",
|
||||
"dnsNameLabel": "[parameters('appName')]"
|
||||
},
|
||||
"osType": "Linux"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"defaultHostName": {
|
||||
"value": "[reference(resourceId('Microsoft.ContainerInstance/containerGroups', parameters('appName'))).ipAddress.fqdn]",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import got from 'got'
|
||||
|
||||
// Will try for 20 minutes, (15 * 80) seconds / 60 [seconds]
|
||||
const RETRIES = 80
|
||||
const DELAY_SECONDS = 15
|
||||
|
||||
/*
|
||||
* Promise resolves once url is healthy or fails if timeout has passed
|
||||
* @param {string} url - health url, e.g. docs.com/healthcheck
|
||||
*/
|
||||
export async function waitUntilUrlIsHealthy(url: string) {
|
||||
try {
|
||||
await got.head(url, {
|
||||
retry: {
|
||||
limit: RETRIES,
|
||||
calculateDelay: ({ computedValue }) => Math.min(computedValue, DELAY_SECONDS * 1000),
|
||||
},
|
||||
})
|
||||
return true
|
||||
} catch {}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user