From aeee76f5ae83b7bb19d66410efcf69a8bad704d1 Mon Sep 17 00:00:00 2001 From: Ben Kogan Date: Thu, 16 Apr 2026 15:19:28 -0700 Subject: [PATCH 1/6] add scos integration --- .github/workflows/scos-daily.yml | 31 ++++++++++++ .github/workflows/scos-integration.yml | 65 ++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 .github/workflows/scos-daily.yml create mode 100644 .github/workflows/scos-integration.yml diff --git a/.github/workflows/scos-daily.yml b/.github/workflows/scos-daily.yml new file mode 100644 index 0000000000..82e2f69c7b --- /dev/null +++ b/.github/workflows/scos-daily.yml @@ -0,0 +1,31 @@ +name: SCOS Daily Integration + +on: + schedule: + - cron: '0 6 * * *' # Daily at 6 AM UTC + workflow_dispatch: + +jobs: + trigger-scos-daily: + name: Trigger SCOS Daily Tests + runs-on: ubuntu-latest + steps: + - name: Trigger SCOS repository_dispatch + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ secrets.SCOS_TRIGGER_PAT }} + repository: snowflake-eng/sas + event-type: snowpark-python-daily + client-payload: | + { + "caller_repo": "${{ github.repository }}", + "caller_sha": "${{ github.sha }}", + "snowpark_python_path": "git+https://github.com/${{ github.repository }}.git@main", + "snowpark_python_branch": "main", + "trigger_type": "daily" + } + + - name: Log trigger + run: | + echo "Triggered SCOS daily tests against snowpark-python main" + echo "See results at: https://github.com/snowflake-eng/sas/actions" diff --git a/.github/workflows/scos-integration.yml b/.github/workflows/scos-integration.yml new file mode 100644 index 0000000000..200e516e01 --- /dev/null +++ b/.github/workflows/scos-integration.yml @@ -0,0 +1,65 @@ +name: SCOS Integration Tests + +on: + pull_request: + branches: [main] + paths: + - 'src/snowflake/snowpark/**' + - 'setup.py' + - 'pyproject.toml' + +jobs: + trigger-scos: + name: Trigger SCOS Tests + runs-on: ubuntu-latest + steps: + - name: Trigger SCOS repository_dispatch + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ secrets.SCOS_TRIGGER_PAT }} + repository: snowflake-eng/sas + event-type: snowpark-python-test + client-payload: | + { + "caller_repo": "${{ github.repository }}", + "caller_sha": "${{ github.event.pull_request.head.sha }}", + "pr_number": "${{ github.event.pull_request.number }}", + "pr_title": ${{ toJSON(github.event.pull_request.title) }}, + "snowpark_python_path": "git+https://github.com/${{ github.repository }}.git@refs/pull/${{ github.event.pull_request.number }}/head", + "snowpark_python_branch": "PR-${{ github.event.pull_request.number }}" + } + + - name: Wait for SCOS status + run: | + echo "SCOS tests triggered. Waiting for status..." + SHA="${{ github.event.pull_request.head.sha }}" + + # Give SCOS a moment to start and post an initial status + sleep 30 + + # Poll for status (timeout after 60 minutes: 120 iterations * 30s) + for i in $(seq 1 120); do + STATUS=$(gh api "repos/${{ github.repository }}/commits/${SHA}/status" \ + -q '.statuses[] | select(.context == "SCOS Integration Tests") | .state' \ + 2>/dev/null | head -n 1 || echo "pending") + + if [ -z "$STATUS" ]; then + STATUS="pending" + fi + + if [ "$STATUS" = "success" ]; then + echo "SCOS tests passed" + exit 0 + elif [ "$STATUS" = "failure" ] || [ "$STATUS" = "error" ]; then + echo "SCOS tests failed (state=$STATUS)" + exit 1 + fi + + echo "Status: $STATUS - waiting 30s... (attempt $i/120)" + sleep 30 + done + + echo "Timeout waiting for SCOS tests" + exit 1 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From dcd9dc20682c9ae1a0b5f9e313186906c9db1b3e Mon Sep 17 00:00:00 2001 From: Ben Kogan Date: Mon, 20 Apr 2026 10:56:00 -0700 Subject: [PATCH 2/6] remove daily --- .github/workflows/scos-daily.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .github/workflows/scos-daily.yml diff --git a/.github/workflows/scos-daily.yml b/.github/workflows/scos-daily.yml deleted file mode 100644 index 82e2f69c7b..0000000000 --- a/.github/workflows/scos-daily.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: SCOS Daily Integration - -on: - schedule: - - cron: '0 6 * * *' # Daily at 6 AM UTC - workflow_dispatch: - -jobs: - trigger-scos-daily: - name: Trigger SCOS Daily Tests - runs-on: ubuntu-latest - steps: - - name: Trigger SCOS repository_dispatch - uses: peter-evans/repository-dispatch@v3 - with: - token: ${{ secrets.SCOS_TRIGGER_PAT }} - repository: snowflake-eng/sas - event-type: snowpark-python-daily - client-payload: | - { - "caller_repo": "${{ github.repository }}", - "caller_sha": "${{ github.sha }}", - "snowpark_python_path": "git+https://github.com/${{ github.repository }}.git@main", - "snowpark_python_branch": "main", - "trigger_type": "daily" - } - - - name: Log trigger - run: | - echo "Triggered SCOS daily tests against snowpark-python main" - echo "See results at: https://github.com/snowflake-eng/sas/actions" From ccb2205e09d2fa25ff05bac632d988042df1cf9d Mon Sep 17 00:00:00 2001 From: Ben Kogan Date: Mon, 20 Apr 2026 11:20:11 -0700 Subject: [PATCH 3/6] better polling --- .github/workflows/scos-integration.yml | 31 ++++++++++++++++++++------ 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/.github/workflows/scos-integration.yml b/.github/workflows/scos-integration.yml index 200e516e01..d940c4bee4 100644 --- a/.github/workflows/scos-integration.yml +++ b/.github/workflows/scos-integration.yml @@ -34,17 +34,26 @@ jobs: echo "SCOS tests triggered. Waiting for status..." SHA="${{ github.event.pull_request.head.sha }}" - # Give SCOS a moment to start and post an initial status - sleep 30 + # Let SCOS's report-pending job post an initial status. + sleep 15 + + # Poll for status (timeout after 60 minutes: 120 iterations * 30s). + # If SCOS hasn't posted any status within the first ~5 minutes, + # assume the dispatch silently failed (bad PAT, wrong repo, etc.) + # and fail fast rather than waiting the full 60 minutes. + seen_status="false" + pending_ack_deadline=10 # iterations before we give up waiting for first status - # Poll for status (timeout after 60 minutes: 120 iterations * 30s) for i in $(seq 1 120); do - STATUS=$(gh api "repos/${{ github.repository }}/commits/${SHA}/status" \ + RAW=$(gh api "repos/${{ github.repository }}/commits/${SHA}/status" \ -q '.statuses[] | select(.context == "SCOS Integration Tests") | .state' \ - 2>/dev/null | head -n 1 || echo "pending") + 2>/dev/null | head -n 1 || true) - if [ -z "$STATUS" ]; then - STATUS="pending" + if [ -n "$RAW" ]; then + seen_status="true" + STATUS="$RAW" + else + STATUS="missing" fi if [ "$STATUS" = "success" ]; then @@ -55,6 +64,14 @@ jobs: exit 1 fi + if [ "$seen_status" = "false" ] && [ "$i" -ge "$pending_ack_deadline" ]; then + echo "No status received from SCOS after ${pending_ack_deadline} polls." + echo "This usually means the repository_dispatch did not reach SCOS." + echo "Check: SCOS_TRIGGER_PAT validity/scope, target repo, and that" + echo "SCOS's precommit.yml has repository_dispatch type 'snowpark-python-test'." + exit 1 + fi + echo "Status: $STATUS - waiting 30s... (attempt $i/120)" sleep 30 done From 3542cf93dd78e566b5be182cb2ca013513721d41 Mon Sep 17 00:00:00 2001 From: Ben Kogan Date: Mon, 20 Apr 2026 13:50:14 -0700 Subject: [PATCH 4/6] dummy workflow test --- .github/workflows/sfdq-e2e-caller.yml | 94 +++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 .github/workflows/sfdq-e2e-caller.yml diff --git a/.github/workflows/sfdq-e2e-caller.yml b/.github/workflows/sfdq-e2e-caller.yml new file mode 100644 index 0000000000..ea4b58b0d9 --- /dev/null +++ b/.github/workflows/sfdq-e2e-caller.yml @@ -0,0 +1,94 @@ +name: SFDQ E2E Caller + +# End-to-end test of the cross-repo CI trigger flow, targeting a dummy +# receiver in snowflake-eng/snowflake-describe-query-analysis (sfdq). +# This is a throwaway workflow to validate the repository_dispatch + +# status-callback plumbing without impacting real SCOS CI. Invoke manually. +on: + workflow_dispatch: + inputs: + sleep_seconds: + description: 'How long the dummy "tests" should run on sfdq.' + type: string + required: false + default: '30' + force_result: + description: 'Force receiver outcome (for testing the fail path).' + type: choice + required: false + default: 'success' + options: + - success + - failure + +jobs: + trigger-and-wait: + name: Trigger sfdq dummy receiver and wait + runs-on: ubuntu-latest + env: + TARGET_REPO: snowflake-eng/snowflake-describe-query-analysis + STATUS_CONTEXT: SFDQ E2E Test + steps: + - name: Dispatch sfdq receiver + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ secrets.SFDQ_TRIGGER_PAT }} + repository: snowflake-eng/snowflake-describe-query-analysis + event-type: snowpark-python-e2e-test + client-payload: | + { + "caller_repo": "${{ github.repository }}", + "caller_sha": "${{ github.sha }}", + "snowpark_python_branch": "${{ github.ref_name }}", + "sleep_seconds": "${{ inputs.sleep_seconds }}", + "force_result": "${{ inputs.force_result }}" + } + + - name: Wait for sfdq status + env: + GH_TOKEN: ${{ github.token }} + SHA: ${{ github.sha }} + run: | + echo "Polling ${{ github.repository }}@${SHA} for context '${STATUS_CONTEXT}'..." + + # Brief delay so the receiver's report-pending job has time to fire. + sleep 15 + + # Up to 30 minutes: 60 iterations * 30s. Dummy tests are short. + # Fail fast if no status appears after ~5 minutes (dispatch lost). + seen_status="false" + pending_ack_deadline=10 + + for i in $(seq 1 60); do + RAW=$(gh api "repos/${{ github.repository }}/commits/${SHA}/status" \ + -q ".statuses[] | select(.context == \"${STATUS_CONTEXT}\") | .state" \ + 2>/dev/null | head -n 1 || true) + + if [ -n "$RAW" ]; then + seen_status="true" + STATUS="$RAW" + else + STATUS="missing" + fi + + if [ "$STATUS" = "success" ]; then + echo "sfdq reported success." + exit 0 + elif [ "$STATUS" = "failure" ] || [ "$STATUS" = "error" ]; then + echo "sfdq reported failure (state=$STATUS)." + exit 1 + fi + + if [ "$seen_status" = "false" ] && [ "$i" -ge "$pending_ack_deadline" ]; then + echo "No status from sfdq after ${pending_ack_deadline} polls." + echo "Likely causes: bad SFDQ_TRIGGER_PAT, wrong event-type, or" + echo "SNOWPARK_PYTHON_STATUS_TOKEN missing on sfdq side." + exit 1 + fi + + echo "Status: $STATUS - waiting 30s... (attempt $i/60)" + sleep 30 + done + + echo "Timeout waiting for sfdq status." + exit 1 From d29bd04182cb8589a32a56fcffe35128f4d1f664 Mon Sep 17 00:00:00 2001 From: Ben Kogan Date: Mon, 20 Apr 2026 13:51:45 -0700 Subject: [PATCH 5/6] clean uip --- .github/workflows/scos-integration.yml | 82 -------------------------- 1 file changed, 82 deletions(-) delete mode 100644 .github/workflows/scos-integration.yml diff --git a/.github/workflows/scos-integration.yml b/.github/workflows/scos-integration.yml deleted file mode 100644 index d940c4bee4..0000000000 --- a/.github/workflows/scos-integration.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: SCOS Integration Tests - -on: - pull_request: - branches: [main] - paths: - - 'src/snowflake/snowpark/**' - - 'setup.py' - - 'pyproject.toml' - -jobs: - trigger-scos: - name: Trigger SCOS Tests - runs-on: ubuntu-latest - steps: - - name: Trigger SCOS repository_dispatch - uses: peter-evans/repository-dispatch@v3 - with: - token: ${{ secrets.SCOS_TRIGGER_PAT }} - repository: snowflake-eng/sas - event-type: snowpark-python-test - client-payload: | - { - "caller_repo": "${{ github.repository }}", - "caller_sha": "${{ github.event.pull_request.head.sha }}", - "pr_number": "${{ github.event.pull_request.number }}", - "pr_title": ${{ toJSON(github.event.pull_request.title) }}, - "snowpark_python_path": "git+https://github.com/${{ github.repository }}.git@refs/pull/${{ github.event.pull_request.number }}/head", - "snowpark_python_branch": "PR-${{ github.event.pull_request.number }}" - } - - - name: Wait for SCOS status - run: | - echo "SCOS tests triggered. Waiting for status..." - SHA="${{ github.event.pull_request.head.sha }}" - - # Let SCOS's report-pending job post an initial status. - sleep 15 - - # Poll for status (timeout after 60 minutes: 120 iterations * 30s). - # If SCOS hasn't posted any status within the first ~5 minutes, - # assume the dispatch silently failed (bad PAT, wrong repo, etc.) - # and fail fast rather than waiting the full 60 minutes. - seen_status="false" - pending_ack_deadline=10 # iterations before we give up waiting for first status - - for i in $(seq 1 120); do - RAW=$(gh api "repos/${{ github.repository }}/commits/${SHA}/status" \ - -q '.statuses[] | select(.context == "SCOS Integration Tests") | .state' \ - 2>/dev/null | head -n 1 || true) - - if [ -n "$RAW" ]; then - seen_status="true" - STATUS="$RAW" - else - STATUS="missing" - fi - - if [ "$STATUS" = "success" ]; then - echo "SCOS tests passed" - exit 0 - elif [ "$STATUS" = "failure" ] || [ "$STATUS" = "error" ]; then - echo "SCOS tests failed (state=$STATUS)" - exit 1 - fi - - if [ "$seen_status" = "false" ] && [ "$i" -ge "$pending_ack_deadline" ]; then - echo "No status received from SCOS after ${pending_ack_deadline} polls." - echo "This usually means the repository_dispatch did not reach SCOS." - echo "Check: SCOS_TRIGGER_PAT validity/scope, target repo, and that" - echo "SCOS's precommit.yml has repository_dispatch type 'snowpark-python-test'." - exit 1 - fi - - echo "Status: $STATUS - waiting 30s... (attempt $i/120)" - sleep 30 - done - - echo "Timeout waiting for SCOS tests" - exit 1 - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 4c7edc1e0d846660f4a7f411ca5c2a9088c70c70 Mon Sep 17 00:00:00 2001 From: Ben Kogan Date: Mon, 20 Apr 2026 13:53:46 -0700 Subject: [PATCH 6/6] change on --- .github/workflows/sfdq-e2e-caller.yml | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/.github/workflows/sfdq-e2e-caller.yml b/.github/workflows/sfdq-e2e-caller.yml index ea4b58b0d9..7fd61ffe71 100644 --- a/.github/workflows/sfdq-e2e-caller.yml +++ b/.github/workflows/sfdq-e2e-caller.yml @@ -5,21 +5,8 @@ name: SFDQ E2E Caller # This is a throwaway workflow to validate the repository_dispatch + # status-callback plumbing without impacting real SCOS CI. Invoke manually. on: - workflow_dispatch: - inputs: - sleep_seconds: - description: 'How long the dummy "tests" should run on sfdq.' - type: string - required: false - default: '30' - force_result: - description: 'Force receiver outcome (for testing the fail path).' - type: choice - required: false - default: 'success' - options: - - success - - failure + pull_request: + branches: [main] jobs: trigger-and-wait: