Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 14 additions & 21 deletions .github/workflows/check-version-bump.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,23 @@ jobs:
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"

- name: Install version comparison dependency
run: python -m pip install packaging

- name: Check that version was bumped if src/ was modified
- name: Reject manual version bumps in pyproject.toml
run: |
BASE_SHA="${{ github.event.pull_request.base.sha }}"
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
MERGE_BASE=$(git merge-base "$BASE_SHA" "$HEAD_SHA")

CHANGED_FILES=$(git diff --name-only "$MERGE_BASE" "$HEAD_SHA")

if echo "$CHANGED_FILES" | grep -qE "^src/.*\.(py|pyi|proto)$"; then
BASE_VERSION=$(git show "$MERGE_BASE:pyproject.toml" | grep "^version = " | cut -d'"' -f2)
HEAD_VERSION=$(git show "$HEAD_SHA:pyproject.toml" | grep "^version = " | cut -d'"' -f2)

# Use PEP 440 ordering because sort -V ranks RCs above final releases
python .github/scripts/check_version_bump.py \
"$BASE_VERSION" "$HEAD_VERSION"

else
echo "No source file changes under src/. Version bump not required."
BASE_VERSION=$(git show "$MERGE_BASE:pyproject.toml" | grep "^version = " | cut -d'"' -f2)
HEAD_VERSION=$(git show "$HEAD_SHA:pyproject.toml" | grep "^version = " | cut -d'"' -f2)

if [[ "$BASE_VERSION" != "$HEAD_VERSION" ]]; then
echo "ERROR: Manual version bump detected in pyproject.toml."
echo " Base: $BASE_VERSION"
echo " PR: $HEAD_VERSION"
echo ""
echo "The version is managed automatically by the release pipeline."
echo "Remove the version change from pyproject.toml and use /release-prep to trigger a release."
exit 1
fi

echo "Version unchanged ($HEAD_VERSION) — OK."
127 changes: 111 additions & 16 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,108 @@ on:
- 'src/**/*.py'
- 'tests/**/*.py'
- 'pyproject.toml'
- 'uv.lock'
push:
branches: [main]
paths:
- '.github/workflows/integration-tests.yml'
- 'src/**/*.py'
- 'tests/**/*.py'
- 'pyproject.toml'
- 'uv.lock'

jobs:
# ── Determine which integration test directories to run ──────────────────────
detect-scope:
name: Detect test scope
runs-on: ubuntu-latest
outputs:
run_all: ${{ steps.scope.outputs.run_all }}
test_paths: ${{ steps.scope.outputs.test_paths }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Compute scope
id: scope
env:
EVENT: ${{ github.event_name }}
run: |
# On push to main or workflow_dispatch → always run everything
if [[ "$EVENT" == "push" || "$EVENT" == "workflow_dispatch" ]]; then
echo "run_all=true" >> "$GITHUB_OUTPUT"
echo "test_paths=tests/*/integration/" >> "$GITHUB_OUTPUT"
echo "Scope: full run (push/workflow_dispatch)"
exit 0
fi

# On pull_request → diff against the base branch
BASE_SHA="${{ github.event.pull_request.base.sha }}"
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
CHANGED=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")

echo "Changed files:"
echo "$CHANGED"

# Global triggers: any of these → run everything
# core/ is foundational — changes there can affect any module
GLOBAL_PATTERNS=(
"pyproject.toml"
"uv.lock"
"src/sap_cloud_sdk/core/"
"tests/core/"
)

for pattern in "${GLOBAL_PATTERNS[@]}"; do
if echo "$CHANGED" | grep -q "^${pattern}"; then
echo "run_all=true" >> "$GITHUB_OUTPUT"
echo "test_paths=tests/*/integration/" >> "$GITHUB_OUTPUT"
echo "Scope: full run (global file changed: $pattern)"
exit 0
fi
done

# Modules that have integration test directories
MODULES_WITH_INTEGRATION=(
adms
agent_memory
agentgateway
aicore
destination
dms
dpi_ng
objectstore
outputmanagement
)

PATHS=()
for module in "${MODULES_WITH_INTEGRATION[@]}"; do
if echo "$CHANGED" | grep -qE "^(src/sap_cloud_sdk|tests)/${module}/"; then
PATHS+=("tests/${module}/integration/")
echo "Module affected: $module"
fi
done

if [[ ${#PATHS[@]} -eq 0 ]]; then
echo "run_all=false" >> "$GITHUB_OUTPUT"
echo "test_paths=" >> "$GITHUB_OUTPUT"
echo "Scope: no integration tests affected — skipping"
else
echo "run_all=false" >> "$GITHUB_OUTPUT"
echo "test_paths=${PATHS[*]}" >> "$GITHUB_OUTPUT"
echo "Scope: ${PATHS[*]}"
fi

# ── Run the integration tests ─────────────────────────────────────────────────
integration-tests:
name: Integration Tests
# Skip integration tests for PRs from forks (they don't have access to secrets)
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
needs: detect-scope
# Skip for fork PRs (no secrets), and skip when no integration tests are affected
if: >
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) &&
needs.detect-scope.outputs.test_paths != ''
runs-on: ${{ contains(github.server_url, 'github.com') && 'ubuntu-latest' || fromJSON('["self-hosted"]') }}
permissions:
contents: read
Expand All @@ -43,21 +132,27 @@ jobs:
run: |
echo "Setting up environment variables for integration tests..."

# Process GitHub secrets (all configuration stored as secrets)
echo '${{ toJSON(secrets) }}' | jq -r 'to_entries[] | select(.key | startswith("CLOUD_SDK_CFG_") or startswith("AICORE_")) | "\(.key)=\(.value)"' | while read line; do
echo "$line" >> $GITHUB_ENV
var_name=$(echo "$line" | cut -d= -f1)
echo "Set secret: $var_name"
done
echo '${{ toJSON(secrets) }}' \
| jq -r 'to_entries[]
| select(.key | startswith("CLOUD_SDK_CFG_") or startswith("AICORE_"))
| "\(.key)=\(.value)"' \
| while read line; do
echo "$line" >> $GITHUB_ENV
echo "Set secret: $(echo "$line" | cut -d= -f1)"
done

# Process GitHub variables (all configuration stored as variables)
echo '${{ toJSON(vars) }}' | jq -r 'to_entries[] | select(.key | startswith("CLOUD_SDK_CFG_") or startswith("AICORE_")) | "\(.key)=\(.value)"' | while read line; do
echo "$line" >> $GITHUB_ENV
var_name=$(echo "$line" | cut -d= -f1)
echo "Set variable: $var_name"
done
echo '${{ toJSON(vars) }}' \
| jq -r 'to_entries[]
| select(.key | startswith("CLOUD_SDK_CFG_") or startswith("AICORE_"))
| "\(.key)=\(.value)"' \
| while read line; do
echo "$line" >> $GITHUB_ENV
echo "Set variable: $(echo "$line" | cut -d= -f1)"
done

echo "Environment setup complete - automatically configured all CLOUD_SDK_CFG_* and AICORE_* environment variables and secrets"
echo "Environment setup complete"

- name: Run integration tests
run: uv run pytest tests/*/integration/ -v --tb=short
env:
TEST_PATHS: ${{ needs.detect-scope.outputs.test_paths }}
run: uv run pytest $TEST_PATHS -v --tb=short
1 change: 1 addition & 0 deletions src/sap_cloud_sdk/destination/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ class DestinationCertificateError(DestinationError):
"""Raised when a client certificate cannot be loaded or wired into the HTTP session."""

pass

Loading