From f9e6a2d3f3a64efccfd8ec9c8f32e28a338d4f8d Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:30:26 -0800 Subject: [PATCH 1/4] Added new GitHub action for manual integration test run based on PR --- .github/workflows/dotnet-build-and-test.yml | 10 +++ .../workflows/integration-tests-manual.yml | 61 +++++++++++++++++++ .github/workflows/python-merge-tests.yml | 13 ++++ 3 files changed, 84 insertions(+) create mode 100644 .github/workflows/integration-tests-manual.yml diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index 95842e703a..2d079f488e 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -7,6 +7,13 @@ name: dotnet-build-and-test on: workflow_dispatch: + workflow_call: + inputs: + checkout-ref: + description: "Git ref to checkout (e.g., a commit SHA from a PR)" + required: false + type: string + default: "" pull_request: branches: ["main", "feature*"] merge_group: @@ -39,6 +46,8 @@ jobs: cosmosDbChanges: ${{ steps.filter.outputs.cosmosdb }} steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.checkout-ref }} - uses: dorny/paths-filter@v3 id: filter with: @@ -76,6 +85,7 @@ jobs: steps: - uses: actions/checkout@v6 with: + ref: ${{ inputs.checkout-ref }} persist-credentials: false sparse-checkout: | . diff --git a/.github/workflows/integration-tests-manual.yml b/.github/workflows/integration-tests-manual.yml new file mode 100644 index 0000000000..0c4c0b4cd4 --- /dev/null +++ b/.github/workflows/integration-tests-manual.yml @@ -0,0 +1,61 @@ +# +# This workflow allows manually running integration tests against an open PR. +# Go to Actions → "Integration Tests (Manual)" → Run workflow → enter the PR number. +# +# It reuses the existing dotnet-build-and-test and python-merge-tests workflows, +# passing the PR's head commit SHA so they check out and test the PR code. +# + +name: Integration Tests (Manual) + +on: + workflow_dispatch: + inputs: + pr-number: + description: "PR number to run integration tests against" + required: true + type: string + +concurrency: + group: integration-tests-manual-pr-${{ github.event.inputs.pr-number }} + cancel-in-progress: true + +jobs: + resolve-pr: + name: Resolve PR + runs-on: ubuntu-latest + outputs: + head-sha: ${{ steps.pr-info.outputs.head-sha }} + steps: + - name: Get PR info + id: pr-info + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR_DATA=$(gh pr view ${{ github.event.inputs.pr-number }} --repo ${{ github.repository }} --json headRefOid,state) + PR_STATE=$(echo "$PR_DATA" | jq -r '.state') + HEAD_SHA=$(echo "$PR_DATA" | jq -r '.headRefOid') + + if [ "$PR_STATE" != "OPEN" ]; then + echo "::error::PR #${{ github.event.inputs.pr-number }} is not open (state: $PR_STATE)" + exit 1 + fi + + echo "head-sha=$HEAD_SHA" >> "$GITHUB_OUTPUT" + echo "Running integration tests for PR #${{ github.event.inputs.pr-number }} at commit $HEAD_SHA" + + dotnet-integration-tests: + name: .NET Integration Tests + needs: resolve-pr + uses: ./.github/workflows/dotnet-build-and-test.yml + with: + checkout-ref: ${{ needs.resolve-pr.outputs.head-sha }} + secrets: inherit + + python-integration-tests: + name: Python Integration Tests + needs: resolve-pr + uses: ./.github/workflows/python-merge-tests.yml + with: + checkout-ref: ${{ needs.resolve-pr.outputs.head-sha }} + secrets: inherit diff --git a/.github/workflows/python-merge-tests.yml b/.github/workflows/python-merge-tests.yml index 7572b0379b..8704ec56c1 100644 --- a/.github/workflows/python-merge-tests.yml +++ b/.github/workflows/python-merge-tests.yml @@ -2,6 +2,13 @@ name: Python - Merge - Tests on: workflow_dispatch: + workflow_call: + inputs: + checkout-ref: + description: "Git ref to checkout (e.g., a commit SHA from a PR)" + required: false + type: string + default: "" pull_request: branches: ["main"] merge_group: @@ -29,6 +36,8 @@ jobs: pythonChanges: ${{ steps.filter.outputs.python}} steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.checkout-ref }} - uses: dorny/paths-filter@v3 id: filter with: @@ -76,6 +85,8 @@ jobs: working-directory: python steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.checkout-ref }} - name: Set up python and install the project id: python-setup uses: ./.github/actions/python-setup @@ -135,6 +146,8 @@ jobs: working-directory: python steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.checkout-ref }} - name: Set up python and install the project id: python-setup uses: ./.github/actions/python-setup From 9c6dc29f75f7a54e57933c05ce16e2f870f05374 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:43:30 -0800 Subject: [PATCH 2/4] Addressed comments --- .../workflows/integration-tests-manual.yml | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/.github/workflows/integration-tests-manual.yml b/.github/workflows/integration-tests-manual.yml index 0c4c0b4cd4..6ed204bab6 100644 --- a/.github/workflows/integration-tests-manual.yml +++ b/.github/workflows/integration-tests-manual.yml @@ -3,7 +3,7 @@ # Go to Actions → "Integration Tests (Manual)" → Run workflow → enter the PR number. # # It reuses the existing dotnet-build-and-test and python-merge-tests workflows, -# passing the PR's head commit SHA so they check out and test the PR code. +# passing a PR ref so they check out and test the PR code. # name: Integration Tests (Manual) @@ -16,6 +16,11 @@ on: required: true type: string +permissions: + contents: read + pull-requests: read + id-token: write + concurrency: group: integration-tests-manual-pr-${{ github.event.inputs.pr-number }} cancel-in-progress: true @@ -25,31 +30,44 @@ jobs: name: Resolve PR runs-on: ubuntu-latest outputs: - head-sha: ${{ steps.pr-info.outputs.head-sha }} + checkout-ref: ${{ steps.pr-info.outputs.checkout-ref }} steps: - name: Get PR info id: pr-info env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - PR_DATA=$(gh pr view ${{ github.event.inputs.pr-number }} --repo ${{ github.repository }} --json headRefOid,state) + PR_NUMBER='${{ github.event.inputs.pr-number }}' + + if ! echo "$PR_NUMBER" | grep -Eq '^[0-9]+$'; then + echo "::error::Invalid PR number '$PR_NUMBER'. Only numeric values are allowed." + exit 1 + fi + + PR_DATA=$(gh pr view "$PR_NUMBER" --repo ${{ github.repository }} --json state,headRepository,headRepositoryOwner) PR_STATE=$(echo "$PR_DATA" | jq -r '.state') - HEAD_SHA=$(echo "$PR_DATA" | jq -r '.headRefOid') + HEAD_OWNER=$(echo "$PR_DATA" | jq -r '.headRepositoryOwner.login') + REPO_OWNER="${{ github.repository_owner }}" if [ "$PR_STATE" != "OPEN" ]; then - echo "::error::PR #${{ github.event.inputs.pr-number }} is not open (state: $PR_STATE)" + echo "::error::PR #$PR_NUMBER is not open (state: $PR_STATE)" + exit 1 + fi + + if [ "$HEAD_OWNER" != "$REPO_OWNER" ]; then + echo "::error::PR #$PR_NUMBER is from a fork ($HEAD_OWNER). Running integration tests against fork PRs is not allowed for security reasons." exit 1 fi - echo "head-sha=$HEAD_SHA" >> "$GITHUB_OUTPUT" - echo "Running integration tests for PR #${{ github.event.inputs.pr-number }} at commit $HEAD_SHA" + echo "checkout-ref=refs/pull/$PR_NUMBER/head" >> "$GITHUB_OUTPUT" + echo "Running integration tests for PR #$PR_NUMBER" dotnet-integration-tests: name: .NET Integration Tests needs: resolve-pr uses: ./.github/workflows/dotnet-build-and-test.yml with: - checkout-ref: ${{ needs.resolve-pr.outputs.head-sha }} + checkout-ref: ${{ needs.resolve-pr.outputs.checkout-ref }} secrets: inherit python-integration-tests: @@ -57,5 +75,5 @@ jobs: needs: resolve-pr uses: ./.github/workflows/python-merge-tests.yml with: - checkout-ref: ${{ needs.resolve-pr.outputs.head-sha }} + checkout-ref: ${{ needs.resolve-pr.outputs.checkout-ref }} secrets: inherit From 09f1ed179127a26138841e12ee83db30af357fd3 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:00:38 -0800 Subject: [PATCH 3/4] Added branch name as input --- .../workflows/integration-tests-manual.yml | 82 ++++++++++++------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/.github/workflows/integration-tests-manual.yml b/.github/workflows/integration-tests-manual.yml index 6ed204bab6..21b55001df 100644 --- a/.github/workflows/integration-tests-manual.yml +++ b/.github/workflows/integration-tests-manual.yml @@ -1,9 +1,9 @@ # -# This workflow allows manually running integration tests against an open PR. -# Go to Actions → "Integration Tests (Manual)" → Run workflow → enter the PR number. +# This workflow allows manually running integration tests against an open PR or a branch. +# Go to Actions → "Integration Tests (Manual)" → Run workflow → enter a PR number or branch name. # # It reuses the existing dotnet-build-and-test and python-merge-tests workflows, -# passing a PR ref so they check out and test the PR code. +# passing a ref so they check out and test the correct code. # name: Integration Tests (Manual) @@ -12,9 +12,15 @@ on: workflow_dispatch: inputs: pr-number: - description: "PR number to run integration tests against" - required: true + description: "PR number to run integration tests against (leave empty if using branch)" + required: false type: string + default: "" + branch: + description: "Branch name to run integration tests against (leave empty if using PR number)" + required: false + type: string + default: "" permissions: contents: read @@ -22,58 +28,74 @@ permissions: id-token: write concurrency: - group: integration-tests-manual-pr-${{ github.event.inputs.pr-number }} + group: integration-tests-manual-${{ github.event.inputs.pr-number || github.event.inputs.branch }} cancel-in-progress: true jobs: - resolve-pr: - name: Resolve PR + resolve-ref: + name: Resolve ref runs-on: ubuntu-latest outputs: - checkout-ref: ${{ steps.pr-info.outputs.checkout-ref }} + checkout-ref: ${{ steps.resolve.outputs.checkout-ref }} steps: - - name: Get PR info - id: pr-info + - name: Resolve checkout ref + id: resolve env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | PR_NUMBER='${{ github.event.inputs.pr-number }}' + BRANCH='${{ github.event.inputs.branch }}' - if ! echo "$PR_NUMBER" | grep -Eq '^[0-9]+$'; then - echo "::error::Invalid PR number '$PR_NUMBER'. Only numeric values are allowed." + if [ -n "$PR_NUMBER" ] && [ -n "$BRANCH" ]; then + echo "::error::Please provide either a PR number or a branch name, not both." exit 1 fi - PR_DATA=$(gh pr view "$PR_NUMBER" --repo ${{ github.repository }} --json state,headRepository,headRepositoryOwner) - PR_STATE=$(echo "$PR_DATA" | jq -r '.state') - HEAD_OWNER=$(echo "$PR_DATA" | jq -r '.headRepositoryOwner.login') - REPO_OWNER="${{ github.repository_owner }}" - - if [ "$PR_STATE" != "OPEN" ]; then - echo "::error::PR #$PR_NUMBER is not open (state: $PR_STATE)" + if [ -z "$PR_NUMBER" ] && [ -z "$BRANCH" ]; then + echo "::error::Please provide either a PR number or a branch name." exit 1 fi - if [ "$HEAD_OWNER" != "$REPO_OWNER" ]; then - echo "::error::PR #$PR_NUMBER is from a fork ($HEAD_OWNER). Running integration tests against fork PRs is not allowed for security reasons." - exit 1 - fi + if [ -n "$PR_NUMBER" ]; then + if ! echo "$PR_NUMBER" | grep -Eq '^[0-9]+$'; then + echo "::error::Invalid PR number '$PR_NUMBER'. Only numeric values are allowed." + exit 1 + fi - echo "checkout-ref=refs/pull/$PR_NUMBER/head" >> "$GITHUB_OUTPUT" - echo "Running integration tests for PR #$PR_NUMBER" + PR_DATA=$(gh pr view "$PR_NUMBER" --repo ${{ github.repository }} --json state,headRepository,headRepositoryOwner) + PR_STATE=$(echo "$PR_DATA" | jq -r '.state') + HEAD_OWNER=$(echo "$PR_DATA" | jq -r '.headRepositoryOwner.login') + REPO_OWNER="${{ github.repository_owner }}" + + if [ "$PR_STATE" != "OPEN" ]; then + echo "::error::PR #$PR_NUMBER is not open (state: $PR_STATE)" + exit 1 + fi + + if [ "$HEAD_OWNER" != "$REPO_OWNER" ]; then + echo "::error::PR #$PR_NUMBER is from a fork ($HEAD_OWNER). Running integration tests against fork PRs is not allowed for security reasons." + exit 1 + fi + + echo "checkout-ref=refs/pull/$PR_NUMBER/head" >> "$GITHUB_OUTPUT" + echo "Running integration tests for PR #$PR_NUMBER" + else + echo "checkout-ref=$BRANCH" >> "$GITHUB_OUTPUT" + echo "Running integration tests for branch $BRANCH" + fi dotnet-integration-tests: name: .NET Integration Tests - needs: resolve-pr + needs: resolve-ref uses: ./.github/workflows/dotnet-build-and-test.yml with: - checkout-ref: ${{ needs.resolve-pr.outputs.checkout-ref }} + checkout-ref: ${{ needs.resolve-ref.outputs.checkout-ref }} secrets: inherit python-integration-tests: name: Python Integration Tests - needs: resolve-pr + needs: resolve-ref uses: ./.github/workflows/python-merge-tests.yml with: - checkout-ref: ${{ needs.resolve-pr.outputs.checkout-ref }} + checkout-ref: ${{ needs.resolve-ref.outputs.checkout-ref }} secrets: inherit From 95094aa3a2a525dfa4141041eec1e2fb302dec31 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:27:22 -0800 Subject: [PATCH 4/4] Small improvements --- .github/workflows/integration-tests-manual.yml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/integration-tests-manual.yml b/.github/workflows/integration-tests-manual.yml index 21b55001df..eb7c9859b4 100644 --- a/.github/workflows/integration-tests-manual.yml +++ b/.github/workflows/integration-tests-manual.yml @@ -42,10 +42,11 @@ jobs: id: resolve env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.inputs.pr-number }} + BRANCH: ${{ github.event.inputs.branch }} + REPO: ${{ github.repository }} + REPO_OWNER: ${{ github.repository_owner }} run: | - PR_NUMBER='${{ github.event.inputs.pr-number }}' - BRANCH='${{ github.event.inputs.branch }}' - if [ -n "$PR_NUMBER" ] && [ -n "$BRANCH" ]; then echo "::error::Please provide either a PR number or a branch name, not both." exit 1 @@ -58,14 +59,13 @@ jobs: if [ -n "$PR_NUMBER" ]; then if ! echo "$PR_NUMBER" | grep -Eq '^[0-9]+$'; then - echo "::error::Invalid PR number '$PR_NUMBER'. Only numeric values are allowed." + echo "::error::Invalid PR number. Only numeric values are allowed." exit 1 fi - PR_DATA=$(gh pr view "$PR_NUMBER" --repo ${{ github.repository }} --json state,headRepository,headRepositoryOwner) + PR_DATA=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json state,headRepository,headRepositoryOwner) PR_STATE=$(echo "$PR_DATA" | jq -r '.state') HEAD_OWNER=$(echo "$PR_DATA" | jq -r '.headRepositoryOwner.login') - REPO_OWNER="${{ github.repository_owner }}" if [ "$PR_STATE" != "OPEN" ]; then echo "::error::PR #$PR_NUMBER is not open (state: $PR_STATE)" @@ -80,6 +80,11 @@ jobs: echo "checkout-ref=refs/pull/$PR_NUMBER/head" >> "$GITHUB_OUTPUT" echo "Running integration tests for PR #$PR_NUMBER" else + if ! echo "$BRANCH" | grep -Eq '^[a-zA-Z0-9_./-]+$'; then + echo "::error::Invalid branch name. Only alphanumeric characters, hyphens, underscores, dots, and slashes are allowed." + exit 1 + fi + echo "checkout-ref=$BRANCH" >> "$GITHUB_OUTPUT" echo "Running integration tests for branch $BRANCH" fi