text: #!/usr/bin/env node
  
  const fs = require('fs')
  const path = require('path')
  const mkdirp = require('mkdirp').sync
  const yaml = require('js-yaml')
  const assert = require('assert')
  const { execSync } = require('child_process')
  const graphqlDataDir = path.join(process.cwd(), 'data/graphql')
  const graphqlStaticDir = path.join(process.cwd(), 'lib/graphql/static')
  const { getContents } = require('../../lib/git-utils')
  const dataFilenames = require('./utils/data-filenames')
  const { oldVersions } = require('../../lib/old-versions-utils')
  const processPreviews = require('./utils/process-previews')
  const processUpcomingChanges = require('./utils/process-upcoming-changes')
  const processSchemas = require('./utils/process-schemas')
  const prerenderObjects = require('./utils/prerender-objects')
  
  // TODO this step is only required as long as we support GHE versions *OLDER THAN* 2.21
  // as soon as 2.20 is deprecated on 2021-02-11, we can remove all graphql-ruby filtering
  const removeHiddenMembersScript = path.join(__dirname, './utils/remove-hidden-schema-members.rb')
  
  // optionally build only 'dotcom' or any GHE version by passing the number ('2.20')
  // TODO need to update this to the new versions
  // for now, fall back to the old versions for use in schema filenames
  let versionsToBuild = oldVersions
  
  if (process.argv.length === 3) {
    const version = process.argv[2]
    assert(oldVersions.includes(version), `'${version}' is not valid! must be one of: ${oldVersions}`)
    versionsToBuild = [version]
  }
  
  main()
  
  async function main () {
    const previewsJson = {}
    const upcomingChangesJson = {}
    const prerenderedObjects = {}
  
    for (const version of versionsToBuild) {
      // 1. UPDATE PREVIEWS
      const previewsPath = getDataFilepath('previews', version)
      const safeForPublicPreviews = yaml.safeLoad(await getRemoteRawContent(previewsPath, version))
      updateFile(previewsPath, yaml.safeDump(safeForPublicPreviews))
      previewsJson[version] = processPreviews(safeForPublicPreviews)
  
      // 2. UPDATE UPCOMING CHANGES
      const upcomingChangesPath = getDataFilepath('upcomingChanges', version)
      const safeForPublicChanges = await getRemoteRawContent(upcomingChangesPath, version)
      updateFile(upcomingChangesPath, safeForPublicChanges)
      upcomingChangesJson[version] = await processUpcomingChanges(safeForPublicChanges)
  
      // 3. UPDATE SCHEMAS
      // note: schemas live in separate files per version
      const schemaPath = getDataFilepath('schemas', version)
      const latestSchema = await getRemoteRawContent(schemaPath, version)
      const safeForPublicSchema = removeHiddenMembers(schemaPath, latestSchema)
      updateFile(schemaPath, safeForPublicSchema)
      const schemaJsonPerVersion = await processSchemas(safeForPublicSchema, safeForPublicPreviews)
      updateStaticFile(schemaJsonPerVersion, path.join(graphqlStaticDir, `schema-${version}.json`))
  
      // 4. PRERENDER OBJECTS HTML
      // because the objects page is too big to render on page load
      prerenderedObjects[version] = await prerenderObjects(schemaJsonPerVersion)
    }
  
    updateStaticFile(previewsJson, path.join(graphqlStaticDir, 'previews.json'))
    updateStaticFile(upcomingChangesJson, path.join(graphqlStaticDir, 'upcoming-changes.json'))
    updateStaticFile(prerenderedObjects, path.join(graphqlStaticDir, 'prerendered-objects.json'))
  }
  
  // get latest from github/github
  async function getRemoteRawContent (filepath, version) {
    const options = {
      owner: 'github',
      repo: 'github',
      ref: version === 'dotcom' ? 'master' : `enterprise-${version}-release`,
      path: `config/${path.basename(filepath)}`
    }
  
    return getContents(...Object.values(options))
  }
  
  function getDataFilepath (id, version) {
    return version === 'dotcom'
      ? path.join(graphqlDataDir, dataFilenames[id].dotcom)
      : path.join(graphqlDataDir, version, dataFilenames[id].enterprise)
  }
  
  function updateFile (filepath, content) {
    console.log(`fetching latest data to ${filepath}`)
    mkdirp(path.dirname(filepath))
    fs.writeFileSync(filepath, content, 'utf8')
  }
  
  function updateStaticFile (json, filepath) {
    const jsonString = JSON.stringify(json, null, 2)
    updateFile(filepath, jsonString)
  }
  
  // run Ruby script to remove featureFlagged directives and other hidden members
  function removeHiddenMembers (schemaPath, latestSchema) {
    // have to write a temp file because the schema is too big to store in memory
    const tempSchemaFilePath = `${schemaPath}-TEMP`
    fs.writeFileSync(tempSchemaFilePath, latestSchema)
    const remoteClean = execSync(`${removeHiddenMembersScript} ${tempSchemaFilePath}`).toString()
    fs.unlinkSync(tempSchemaFilePath)
  
    return remoteClean
  }
  