From 1eddeb071f9653a8df7279b20c843b1663a5a042 Mon Sep 17 00:00:00 2001 From: haseebmalik18 Date: Wed, 22 Jul 2026 11:42:13 -0400 Subject: [PATCH 1/3] Add e2e workflow that triggers the sandbox run on pull requests --- .github/workflows/e2e.yml | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/e2e.yml diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 00000000..27a9429a --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,50 @@ +name: End-to-End + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + contents: read + +concurrency: + group: e2e-${{ github.event.pull_request.number }} + cancel-in-progress: true + +env: + SANDBOX_REPO: DSACMS/codejson-action-e2e + SANDBOX_WORKFLOW: e2e.yml + +jobs: + e2e: + name: End-to-End + runs-on: ubuntu-latest + timeout-minutes: 30 + + # Fork PRs get no secrets, so they can't reach the sandbox. Skip rather than fail + # the check, and run the e2e by hand for those. + if: github.event.pull_request.head.repo.full_name == github.repository + + steps: + - name: Trigger sandbox run + id: trigger + env: + GH_TOKEN: ${{ secrets.SANDBOX_E2E_TOKEN }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + set -euo pipefail + + # workflow_dispatch doesn't return the run it created, so tag the run with an + # id only this job could produce. + RUN_NAME="e2e ${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" + echo "run_name=${RUN_NAME}" >> "$GITHUB_OUTPUT" + + gh workflow run "$SANDBOX_WORKFLOW" \ + --repo "$SANDBOX_REPO" \ + --ref main \ + -f action_sha="$HEAD_SHA" \ + -f pr_number="$PR_NUMBER" \ + -f correlation_id="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" + + echo "Dispatched ${SANDBOX_REPO} for ${HEAD_SHA} (PR #${PR_NUMBER})" From 74e915c4832cac67217a3a08eea0400e1a8263a6 Mon Sep 17 00:00:00 2001 From: haseebmalik18 Date: Wed, 22 Jul 2026 13:36:01 -0400 Subject: [PATCH 2/3] Wait for the sandbox run and report its result --- .github/workflows/e2e.yml | 42 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 27a9429a..6625af0d 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -36,7 +36,7 @@ jobs: set -euo pipefail # workflow_dispatch doesn't return the run it created, so tag the run with an - # id only this job could produce. + # id only this job could produce and find it by name below. RUN_NAME="e2e ${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" echo "run_name=${RUN_NAME}" >> "$GITHUB_OUTPUT" @@ -48,3 +48,43 @@ jobs: -f correlation_id="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" echo "Dispatched ${SANDBOX_REPO} for ${HEAD_SHA} (PR #${PR_NUMBER})" + + - name: Wait for sandbox verdict + env: + GH_TOKEN: ${{ secrets.SANDBOX_E2E_TOKEN }} + RUN_NAME: ${{ steps.trigger.outputs.run_name }} + run: | + set -euo pipefail + + RUN_ID="" + for _ in $(seq 1 24); do + RUN_ID=$(gh api "repos/${SANDBOX_REPO}/actions/workflows/${SANDBOX_WORKFLOW}/runs?per_page=100" \ + --jq "[.workflow_runs[] | select(.name == \"${RUN_NAME}\" or .display_title == \"${RUN_NAME}\")] | .[0].id // empty") + [ -n "$RUN_ID" ] && break + sleep 5 + done + + if [ -z "$RUN_ID" ]; then + echo "::error::Sandbox run '${RUN_NAME}' never appeared in ${SANDBOX_REPO}." + exit 1 + fi + + RUN_URL="https://github.com/${SANDBOX_REPO}/actions/runs/${RUN_ID}" + echo "Sandbox run: ${RUN_URL}" + echo "Sandbox run: ${RUN_URL}" >> "$GITHUB_STEP_SUMMARY" + + # The sandbox serializes its runs, so this can sit queued behind another PR. + while true; do + STATUS=$(gh api "repos/${SANDBOX_REPO}/actions/runs/${RUN_ID}" --jq '.status') + [ "$STATUS" = "completed" ] && break + echo " ${STATUS}..." + sleep 15 + done + + CONCLUSION=$(gh api "repos/${SANDBOX_REPO}/actions/runs/${RUN_ID}" --jq '.conclusion') + + if [ "$CONCLUSION" != "success" ]; then + echo "::error::Sandbox e2e ${CONCLUSION}. See ${RUN_URL}" + exit 1 + fi + echo "Sandbox e2e passed." From 818079fce39eb8a5cda761d5be75a9c22b4fd379 Mon Sep 17 00:00:00 2001 From: haseebmalik18 Date: Thu, 30 Jul 2026 14:30:15 -0400 Subject: [PATCH 3/3] Mint the sandbox token from a GitHub App instead of a PAT --- .github/workflows/e2e.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 6625af0d..20d156d1 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -26,10 +26,19 @@ jobs: if: github.event.pull_request.head.repo.full_name == github.repository steps: + - name: Generate sandbox token + id: app_token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ secrets.E2E_APP_ID }} + private-key: ${{ secrets.E2E_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: codejson-action-e2e + - name: Trigger sandbox run id: trigger env: - GH_TOKEN: ${{ secrets.SANDBOX_E2E_TOKEN }} + GH_TOKEN: ${{ steps.app_token.outputs.token }} HEAD_SHA: ${{ github.event.pull_request.head.sha }} PR_NUMBER: ${{ github.event.pull_request.number }} run: | @@ -51,7 +60,7 @@ jobs: - name: Wait for sandbox verdict env: - GH_TOKEN: ${{ secrets.SANDBOX_E2E_TOKEN }} + GH_TOKEN: ${{ steps.app_token.outputs.token }} RUN_NAME: ${{ steps.trigger.outputs.run_name }} run: | set -euo pipefail