1
0
mirror of synced 2025-12-21 02:46:50 -05:00
Files
docs/script/early-access/clone-for-build.js
James M. Greene b2c8af9d31 Secure early access staging deployment (#21450)
* Explicitly ensure the early access parent directories are created when cloning

* Use explicit --file flag with tar

* Remove security hole for Staging deployment by concatenating archives

* Fail the staging builds if *.js, .npmrc, or Procfile is changed in the open source repo

* docker build: extract user-code in separate directory

* Checkout PR base branch and install dependencies

* Remove one-off package installs

* Remove selective file checkout

* Don't persist git cloning credentials

It usually makes the clone of early access content fail in the later steps

* Update .github/workflows/staging-deploy-pr-docker.yml

Co-authored-by: James M. Greene <JamesMGreene@github.com>

* Update .github/workflows/staging-deploy-pr-docker.yml

Co-authored-by: James M. Greene <JamesMGreene@github.com>

* Remove debugging step

* Best practice: Use environment variables to avoid potential injection attacks if the data was user-controlled

Co-authored-by: Mike Surowiec <mikesurowiec@users.noreply.github.com>
Co-authored-by: Robert Sese <sese@github.com>
Co-authored-by: Robert Sese <robert.sese@gmail.com>
2021-09-17 22:41:13 +00:00

141 lines
4.6 KiB
JavaScript
Executable File

#!/usr/bin/env node
// [start-readme]
//
// This script is run as a postbuild script during staging and deployments on Heroku. It clones a branch
// in the early-access repo that matches the current branch in the docs repo; if one can't be found, it
// clones the `main` branch.
//
// [end-readme]
import dotenv from 'dotenv'
import { execSync } from 'child_process'
import rimraf from 'rimraf'
import fs from 'fs'
import path from 'path'
import os from 'os'
dotenv.config()
const {
DOCUBOT_REPO_PAT,
HEROKU_PRODUCTION_APP,
GIT_BRANCH, // Set by Actions and/or the deployer with the name of the docs-internal branch
} = process.env
// Exit if PAT is not found
if (!DOCUBOT_REPO_PAT) {
console.log('Skipping early access, not authorized')
process.exit(0)
}
const EA_PRODUCTION_BRANCH = 'main'
// If a branch name is not provided in the environment, attempt to get
// the local branch name; or default to 'main'
let currentBranch = (GIT_BRANCH || '').replace(/^refs\/heads\//, '')
if (!currentBranch) {
try {
currentBranch = execSync('git branch --show-current').toString()
} catch (err) {
// Ignore but log
console.warn('Error checking for local branch:', err.message)
}
}
if (!currentBranch) {
currentBranch = EA_PRODUCTION_BRANCH
}
// Early Access details
const earlyAccessOwner = 'github'
const earlyAccessRepoName = 'docs-early-access'
const earlyAccessDirName = 'early-access'
const earlyAccessFullRepo = `https://${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessRepoName}`
// On our Azure self-hosted runners, os.tmpdir() doesn't work reliably. On Heroku, os.homedir doesn't work reliably.
const earlyAccessCloningParentDir = process.env.CI ? os.homedir() : os.tmpdir()
const earlyAccessCloningDir = path.join(earlyAccessCloningParentDir, earlyAccessRepoName)
const destinationDirNames = ['content', 'data', 'assets/images']
const destinationDirsMap = destinationDirNames.reduce((map, dirName) => {
map[dirName] = path.join(process.cwd(), dirName, earlyAccessDirName)
return map
}, {})
// Production vs. staging environment
// TODO test that this works as expected
const environment = HEROKU_PRODUCTION_APP ? 'production' : 'staging'
// Early access branch to clone
let earlyAccessBranch = HEROKU_PRODUCTION_APP ? EA_PRODUCTION_BRANCH : currentBranch
// Confirm that the branch exists in the remote
let branchExists = execSync(
`git ls-remote --heads ${earlyAccessFullRepo} ${earlyAccessBranch}`
).toString()
// If the branch did NOT exist, try checking for the default branch instead
if (!branchExists && earlyAccessBranch !== EA_PRODUCTION_BRANCH) {
console.warn(
`The branch '${earlyAccessBranch}' was not found in ${earlyAccessOwner}/${earlyAccessRepoName}!`
)
console.warn(`Attempting the default branch ${EA_PRODUCTION_BRANCH} instead...`)
earlyAccessBranch = EA_PRODUCTION_BRANCH
branchExists = execSync(
`git ls-remote --heads ${earlyAccessFullRepo} ${earlyAccessBranch}`
).toString()
}
// If no suitable branch was found, bail out now
if (!branchExists) {
console.error(
`The branch '${earlyAccessBranch}' was not found in ${earlyAccessOwner}/${earlyAccessRepoName}!`
)
console.error('Exiting!')
process.exit(1)
}
// Remove any previously cloned copies of the early access repo
rimraf.sync(earlyAccessCloningDir)
// Clone the repo
console.log(`Setting up: ${earlyAccessCloningDir}`)
execSync(
`git clone --single-branch --branch ${earlyAccessBranch} ${earlyAccessFullRepo} ${earlyAccessRepoName}`,
{
cwd: earlyAccessCloningParentDir,
}
)
console.log(`Using early-access ${environment} branch: '${earlyAccessBranch}'`)
// Remove all existing early access directories from this repo
destinationDirNames.forEach((key) => rimraf.sync(destinationDirsMap[key]))
// Move the latest early access source directories into this repo
destinationDirNames.forEach((dirName) => {
const sourceDir = path.join(earlyAccessCloningDir, dirName)
const destDir = destinationDirsMap[dirName]
// If the source directory doesn't exist, skip it
if (!fs.existsSync(sourceDir)) {
console.warn(`Early access directory '${dirName}' does not exist. Skipping...`)
return
}
// Ensure the base directory exists
fs.mkdirSync(path.join(process.cwd(), dirName), { recursive: true })
// Move the directory from the cloned source to the destination
fs.renameSync(sourceDir, destDir)
// Confirm the newly moved directory exist
if (fs.existsSync(destDir)) {
console.log(`Successfully moved early access directory '${dirName}' into this repo`)
} else {
throw new Error(`Failed to move early access directory '${dirName}'!`)
}
})
// Remove the source content again for good hygiene
rimraf.sync(earlyAccessCloningDir)