diff --git a/README.md b/README.md index 09b27085d..81f7f0c9c 100644 --- a/README.md +++ b/README.md @@ -1462,6 +1462,47 @@ jobs: publish-summary: false ``` +### Automatic PR number & URL detection + +When recording runs to Cypress Cloud, the PR number and URL can be automatically detected if you pass `GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}` +via the workflow `env`. When set, this value enables the Action to perform additional logic that grabs the related PR number and URL (if they +exist) and sets them in the environment variables `CYPRESS_PULL_REQUEST_ID` and `CYPRESS_PULL_REQUEST_URL`, respectively. +* See Cypress' documentation on [CI Build Information](https://on.cypress.io/guides/continuous-integration/introduction#CI-Build-Information) + +Example workflow using the variables: +```yml +name: Example echo PR number and URL +on: push +jobs: + cypress-run: + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Cypress run + uses: cypress-io/github-action@v6 + with: + record: true + - run: echo "PR number is $CYPRESS_PULL_REQUEST_ID" + - run: echo "PR URL is $CYPRESS_PULL_REQUEST_URL" + env: + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` + +#### Triggering event: `pull_request`/`pull_request_target` + +For either of these events, we set `CYPRESS_PULL_REQUEST_ID` and `CYPRESS_PULL_REQUEST_URL` to that of the PR number and URL, respectively, of the +PR that triggered the workflow. + +#### Triggering event: `push` + +When a commit on a branch without a PR is made, the Cypress GitHub Action checks to see if the commit that triggered the workflow has a +related PR. If the commit exists in any other PRs, it's considered a related PR. When there are related PRs, we grab the first related PR +and use that PR's number and URL for `CYPRESS_PULL_REQUEST_ID` and `CYPRESS_PULL_REQUEST_URL`, respectively. + +If no related PR is detected, `CYPRESS_PULL_REQUEST_ID` and `CYPRESS_PULL_REQUEST_URL` will be undefined. + ## Node.js ### Support diff --git a/dist/index.js b/dist/index.js index b15cc66b7..80ce2b240 100644 --- a/dist/index.js +++ b/dist/index.js @@ -74832,6 +74832,80 @@ const waitOnMaybe = () => { const I = (x) => x +const detectPrNumber = async () => { + const { + GITHUB_SHA, + GITHUB_TOKEN, + GITHUB_RUN_ID, + GITHUB_REPOSITORY, + GITHUB_HEAD_REF, + GITHUB_REF, + GITHUB_SERVER_URL, + CYPRESS_PULL_REQUEST_ID, + CYPRESS_PULL_REQUEST_URL + } = process.env + + if (CYPRESS_PULL_REQUEST_ID && CYPRESS_PULL_REQUEST_URL) { + // Both pull request envs are already defined - no need to do anything else + return + } + + const [owner, repo] = GITHUB_REPOSITORY.split('/') + let prNumber + + if (GITHUB_TOKEN) { + debug( + `Detecting PR number by asking GitHub about run ${GITHUB_RUN_ID}` + ) + + const client = new Octokit({ + auth: GITHUB_TOKEN + }) + + if (GITHUB_HEAD_REF) { + // GITHUB_HEAD_REF is only defined when the event that triggered it was 'pull_request' or 'pull_request_target' (meaning a PR number should be readily-available) + // should have format refs/pull//merge when triggered by pull_request workflow + prNumber = parseInt(GITHUB_REF.split('/')[2]) + } else { + try { + const resp = await client.request( + 'GET /repos/:owner/:repo/commits/:commit_sha/pulls', + { + owner, + repo, + commit_sha: GITHUB_SHA + } + ) + + if ( + resp && + resp.data && + resp.data[0] && + resp.data[0].number + ) { + prNumber = resp.data[0].number + } + } catch (e) { + console.error( + `Unable to fetch related PR data for commit: '${GITHUB_SHA}': `, + e + ) + } + } + + if (prNumber) { + if (!CYPRESS_PULL_REQUEST_ID) { + core.exportVariable('CYPRESS_PULL_REQUEST_ID', prNumber) + } + + const url = `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pull/${prNumber}` + if (!CYPRESS_PULL_REQUEST_URL) { + core.exportVariable('CYPRESS_PULL_REQUEST_URL', url) + } + } + } +} + /** * Asks Cypress API if there were already builds for this commit. * In that case increments the count to get unique parallel id. @@ -75065,6 +75139,8 @@ const runTests = async () => { core.exportVariable('CYPRESS_CACHE_FOLDER', CYPRESS_CACHE_FOLDER) core.exportVariable('TERM', 'xterm') + await detectPrNumber() + if (customCommand) { console.log('Using custom test command: %s', customCommand) return execCommand(customCommand, true, 'run tests') diff --git a/index.js b/index.js index 81311a294..2b550e382 100644 --- a/index.js +++ b/index.js @@ -436,6 +436,80 @@ const waitOnMaybe = () => { const I = (x) => x +const detectPrNumber = async () => { + const { + GITHUB_SHA, + GITHUB_TOKEN, + GITHUB_RUN_ID, + GITHUB_REPOSITORY, + GITHUB_HEAD_REF, + GITHUB_REF, + GITHUB_SERVER_URL, + CYPRESS_PULL_REQUEST_ID, + CYPRESS_PULL_REQUEST_URL + } = process.env + + if (CYPRESS_PULL_REQUEST_ID && CYPRESS_PULL_REQUEST_URL) { + // Both pull request envs are already defined - no need to do anything else + return + } + + const [owner, repo] = GITHUB_REPOSITORY.split('/') + let prNumber + + if (GITHUB_TOKEN) { + debug( + `Detecting PR number by asking GitHub about run ${GITHUB_RUN_ID}` + ) + + const client = new Octokit({ + auth: GITHUB_TOKEN + }) + + if (GITHUB_HEAD_REF) { + // GITHUB_HEAD_REF is only defined when the event that triggered it was 'pull_request' or 'pull_request_target' (meaning a PR number should be readily-available) + // should have format refs/pull//merge when triggered by pull_request workflow + prNumber = parseInt(GITHUB_REF.split('/')[2]) + } else { + try { + const resp = await client.request( + 'GET /repos/:owner/:repo/commits/:commit_sha/pulls', + { + owner, + repo, + commit_sha: GITHUB_SHA + } + ) + + if ( + resp && + resp.data && + resp.data[0] && + resp.data[0].number + ) { + prNumber = resp.data[0].number + } + } catch (e) { + console.error( + `Unable to fetch related PR data for commit: '${GITHUB_SHA}': `, + e + ) + } + } + + if (prNumber) { + if (!CYPRESS_PULL_REQUEST_ID) { + core.exportVariable('CYPRESS_PULL_REQUEST_ID', prNumber) + } + + const url = `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pull/${prNumber}` + if (!CYPRESS_PULL_REQUEST_URL) { + core.exportVariable('CYPRESS_PULL_REQUEST_URL', url) + } + } + } +} + /** * Asks Cypress API if there were already builds for this commit. * In that case increments the count to get unique parallel id. @@ -669,6 +743,8 @@ const runTests = async () => { core.exportVariable('CYPRESS_CACHE_FOLDER', CYPRESS_CACHE_FOLDER) core.exportVariable('TERM', 'xterm') + await detectPrNumber() + if (customCommand) { console.log('Using custom test command: %s', customCommand) return execCommand(customCommand, true, 'run tests')