diff --git a/.github/workflows/staging-deploy-pr.yml b/.github/workflows/staging-deploy-pr.yml index b7020869ed..03f1624c50 100644 --- a/.github/workflows/staging-deploy-pr.yml +++ b/.github/workflows/staging-deploy-pr.yml @@ -11,15 +11,97 @@ on: - reopened - synchronize - unlocked + workflow_dispatch: + inputs: + pullRequestUrl: + description: 'Pull Request URL' + required: true + default: 'https://github.com/github/docs/pull/1234' + forceRebuild: + description: 'Force the Heroku App to be rebuilt from scratch? (true/false)' + required: false + default: 'false' jobs: + validate-inputs: + if: ${{ github.repository == 'github/docs-internal' || github.repository == 'github/docs' }} + name: Validate inputs + runs-on: ubuntu-latest + timeout-minutes: 2 + outputs: + headRef: ${{ steps.validate.outputs.headRef }} + steps: + - if: ${{ github.event_name == 'workflow_dispatch' }} + name: Check out repo + uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f + with: + # Enables cloning the Early Access repo later with the relevant PAT + persist-credentials: 'false' + + - if: ${{ github.event_name == 'workflow_dispatch' }} + name: Setup node + uses: actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e + with: + node-version: 16.x + + - if: ${{ github.event_name == 'workflow_dispatch' }} + name: Get npm cache directory + id: npm-cache + run: | + echo "::set-output name=dir::$(npm config get cache)" + + - if: ${{ github.event_name == 'workflow_dispatch' }} + name: Cache node modules + uses: actions/cache@0781355a23dac32fd3bac414512f4b903437991a + with: + path: ${{ steps.npm-cache.outputs.dir }} + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - if: ${{ github.event_name == 'workflow_dispatch' }} + name: Install dependencies + run: npm ci + + - if: ${{ github.event_name == 'workflow_dispatch' }} + name: Validate and get head.ref + id: validate + uses: actions/github-script@2b34a689ec86a68d8ab9478298f91d5401337b7d + env: + PR_URL: ${{ github.event.inputs.pullRequestUrl }} + FORCE_REBUILD: ${{ github.event.inputs.forceRebuild }} + with: + script: | + const parsePrUrl = require('./script/deployment/parse-pr-url') + + // Manually resolve workflow_dispatch inputs + const { PR_URL, FORCE_REBUILD } = process.env + + if (!['true', 'false'].includes(FORCE_REBUILD)) { + throw new Error(`'forceRebuild' input must be either 'true' or 'false' but was '${FORCE_REBUILD}'`) + } + + const { owner, repo, pullNumber } = parsePrUrl(PR_URL) + if (!owner || !repo || !pullNumber) { + throw new Error(`'pullRequestUrl' input must match URL format 'https://github.com/github/(docs|docs-internal)/pull/123' but was '${PR_URL}'`) + } + + const { data: pullRequest } = await github.pulls.get({ + owner, + repo, + pull_number: pullNumber + }) + + core.setOutput('headRef', pullRequest.head.ref) + deploy: if: ${{ github.repository == 'github/docs-internal' || github.repository == 'github/docs' }} + needs: validate-inputs name: Deploy runs-on: ubuntu-latest timeout-minutes: 10 concurrency: - group: staging_${{ github.head_ref }} + group: staging_${{ needs.validate-inputs.outputs.headRef || github.head_ref }} cancel-in-progress: true steps: - name: Check out repo @@ -57,6 +139,8 @@ jobs: DOCUBOT_REPO_PAT: ${{ secrets.DOCUBOT_REPO_PAT }} HYDRO_ENDPOINT: ${{ secrets.HYDRO_ENDPOINT }} HYDRO_SECRET: ${{ secrets.HYDRO_SECRET }} + PR_URL: ${{ github.event.inputs.pullRequestUrl }} + FORCE_REBUILD: ${{ github.event.inputs.forceRebuild }} with: script: | const { GITHUB_TOKEN, HEROKU_API_TOKEN } = process.env @@ -71,6 +155,7 @@ jobs: throw new Error('You must supply a HEROKU_API_TOKEN environment variable!') } + const parsePrUrl = require('./script/deployment/parse-pr-url') const getOctokit = require('./script/helpers/github') const deployToStaging = require('./script/deployment/deploy-to-staging') @@ -80,10 +165,33 @@ jobs: const octokit = getOctokit() try { + let pullRequest = null + let forceRebuild = false + + // Manually resolve workflow_dispatch inputs + if (context.eventName === 'workflow_dispatch') { + const { PR_URL, FORCE_REBUILD } = process.env + + forceRebuild = FORCE_REBUILD === 'true' + + const { owner, repo, pullNumber } = parsePrUrl(PR_URL) + if (!owner || !repo || !pullNumber) { + throw new Error(`'pullRequestUrl' input must match URL format 'https://github.com/github/(docs|docs-internal)/pull/123' but was '${PR_URL}'`) + } + + const { data: pr } = await octokit.pulls.get({ + owner, + repo, + pull_number: pullNumber + }) + pullRequest = pr + } + await deployToStaging({ herokuToken: HEROKU_API_TOKEN, octokit, - pullRequest: context.payload.pull_request, + pullRequest: pullRequest || context.payload.pull_request, + forceRebuild, runId: context.runId }) } catch (error) {