Skip to content

Commit 81e424b

Browse files
authored
chore: ci workflows v3 (#2238)
chore(ci): update CI workflow - separate environment for functional tests requiring external API secrets - updated Makefile and workflow - updated test markers in packages to enable functional testing
1 parent 6ada999 commit 81e424b

11 files changed

Lines changed: 351 additions & 354 deletions

File tree

.github/workflows/ci.yml

Lines changed: 42 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
name: Full CI
1+
name: CI
2+
23
on:
34
push:
4-
branches:
5-
- main
6-
- dev
5+
branches: [main]
76
pull_request:
87
workflow_dispatch:
98

@@ -12,89 +11,57 @@ concurrency:
1211
cancel-in-progress: true
1312

1413
jobs:
15-
detect-changes:
14+
lint:
1615
runs-on: ubuntu-latest
17-
outputs:
18-
giskard-core: ${{ steps.filter.outputs.giskard-core }}
19-
giskard-agents: ${{ steps.filter.outputs.giskard-agents }}
20-
giskard-checks: ${{ steps.filter.outputs.giskard-checks }}
16+
timeout-minutes: 10
2117
steps:
22-
- name: Checkout code
23-
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
24-
25-
- name: Detect changed libraries
26-
uses: dorny/paths-filter@v3
27-
id: filter
18+
- uses: actions/checkout@v4
19+
- uses: astral-sh/setup-uv@v7
2820
with:
29-
filters: |
30-
giskard-core:
31-
- 'libs/giskard-core/**'
32-
giskard-agents:
33-
- 'libs/giskard-agents/**'
34-
giskard-checks:
35-
- 'libs/giskard-checks/**'
21+
enable-cache: true
22+
python-version: "3.12"
23+
- run: make install install-tools
24+
- run: make check
3625

37-
pre_checks:
26+
test-unit:
27+
needs: lint
3828
runs-on: ubuntu-latest
39-
timeout-minutes: 30
29+
timeout-minutes: 15
30+
strategy:
31+
fail-fast: false
32+
matrix:
33+
python-version: ["3.12", "3.13"]
34+
package: [giskard-core, giskard-agents, giskard-checks]
35+
name: test-unit / ${{ matrix.package }} / ${{ matrix.python-version }}
4036
steps:
41-
- name: Checkout code
42-
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
43-
44-
- name: Install uv
45-
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7
46-
47-
- name: Setup development environment
48-
run: make setup
49-
50-
- name: Run all checks
51-
run: make check
37+
- uses: actions/checkout@v4
38+
- uses: astral-sh/setup-uv@v7
39+
with:
40+
enable-cache: true
41+
python-version: ${{ matrix.python-version }}
42+
- run: make install
43+
- run: make test-unit PACKAGE=${{ matrix.package }}
5244

53-
test:
45+
test-functional:
46+
needs: lint
5447
runs-on: ubuntu-latest
55-
needs: [detect-changes]
56-
if: |
57-
github.ref == 'refs/heads/main' ||
58-
needs.detect-changes.outputs.giskard-core == 'true' ||
59-
needs.detect-changes.outputs.giskard-agents == 'true' ||
60-
needs.detect-changes.outputs.giskard-checks == 'true'
61-
env:
62-
TEST_MODEL: "gemini/gemini-2.0-flash"
63-
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
48+
environment: CI
6449
timeout-minutes: 30
6550
strategy:
66-
fail-fast: true
51+
fail-fast: false
6752
matrix:
6853
python-version: ["3.12", "3.13"]
54+
package: [giskard-core, giskard-agents, giskard-checks]
55+
name: test-functional / ${{ matrix.package }} / ${{ matrix.python-version }}
56+
env:
57+
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
58+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
59+
TEST_MODEL: "gemini/gemini-2.0-flash"
6960
steps:
70-
- name: Checkout code
71-
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
72-
73-
- name: Install uv and set the python version
74-
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7
61+
- uses: actions/checkout@v4
62+
- uses: astral-sh/setup-uv@v7
7563
with:
64+
enable-cache: true
7665
python-version: ${{ matrix.python-version }}
77-
78-
- name: Setup development environment
79-
run: make setup
80-
81-
- name: Run tests for giskard-core
82-
if: |
83-
github.ref == 'refs/heads/main' ||
84-
needs.detect-changes.outputs.giskard-core == 'true'
85-
run: make test-giskard-core
86-
87-
- name: Run tests for giskard-agents
88-
if: |
89-
github.ref == 'refs/heads/main' ||
90-
needs.detect-changes.outputs.giskard-core == 'true' ||
91-
needs.detect-changes.outputs.giskard-agents == 'true'
92-
run: make test-giskard-agents
93-
94-
- name: Run tests for giskard-checks
95-
if: |
96-
github.ref == 'refs/heads/main' ||
97-
needs.detect-changes.outputs.giskard-core == 'true' ||
98-
needs.detect-changes.outputs.giskard-agents == 'true' ||
99-
needs.detect-changes.outputs.giskard-checks == 'true'
100-
run: make test-giskard-checks
66+
- run: make install
67+
- run: make test-functional PACKAGE=${{ matrix.package }}

Makefile

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
# Variables
22
LIBS := giskard-core giskard-agents giskard-checks
3-
TEST_TARGETS := $(addprefix test-,$(LIBS))
3+
PACKAGE ?= # Optional package to test (e.g., giskard-core, giskard-agents, giskard-checks)
44

55
# Default target
66
help: ## Show this help message
77
@echo "Available targets:"
88
@grep -E '^[a-zA-Z0-9_%-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
9+
910
# Installation targets
1011
install: ## Install project dependencies
1112
uv sync
1213

13-
sync: install ## Alias for install
14-
1514
install-tools: ## Install development tools
1615
uv tool install ruff
1716
uv tool install vermin
@@ -23,13 +22,26 @@ pre-commit-install: ## Setup pre-commit hooks
2322

2423
setup: install install-tools pre-commit-install ## Complete development setup (install deps + tools)
2524

26-
test: $(TEST_TARGETS) ## Run all tests
27-
28-
$(TEST_TARGETS): test-%: ## Run tests for a specific library (e.g., make test-giskard-core)
29-
@echo "------------------------------------------------"
30-
@echo "Running tests for $*"
31-
@echo "------------------------------------------------"
32-
uv run pytest libs/$*
25+
test: ## Run all tests (unit + functional), optional PACKAGE=<name>
26+
ifdef PACKAGE
27+
uv run pytest libs/$(PACKAGE)
28+
else
29+
uv run pytest libs/
30+
endif
31+
32+
test-unit: ## Run unit tests only (excludes functional), optional PACKAGE=<name>
33+
ifdef PACKAGE
34+
uv run pytest libs/$(PACKAGE) -m "not functional"
35+
else
36+
$(foreach lib,$(LIBS),uv run pytest libs/$(lib) -m "not functional" &&) true
37+
endif
38+
39+
test-functional: ## Run functional tests only (requires API keys), optional PACKAGE=<name>
40+
ifdef PACKAGE
41+
uv run pytest libs/$(PACKAGE) -m "functional"
42+
else
43+
$(foreach lib,$(LIBS),uv run pytest libs/$(lib) -m "functional" &&) true
44+
endif
3345

3446
test-package-conflict: ## Test package conflict with giskard legacy package installed
3547
@echo "Testing package conflict..."
@@ -67,7 +79,7 @@ typecheck: ## Run type checking with basedpyright
6779
uv tool run basedpyright --level error .
6880

6981
security: ## Check for security vulnerabilities
70-
uv run pip-audit
82+
uv run pip-audit --skip-editable
7183

7284
generate-licenses: ## Generate licenses
7385
uv tool run licensecheck --license MIT \
@@ -79,13 +91,7 @@ check-licenses: ## Check for licenses
7991
--show-only-failing --zero \
8092
--skip-dependencies giskard-agents giskard-checks giskard-core
8193

82-
# Combined targets
83-
check: lint check-format check-compat typecheck security check-licenses ## Run all checks (lint, format, compatibility, typecheck)
84-
85-
all: format check test ## Format, check, and test
86-
87-
# CI simulation
88-
ci: check test ## Run the same checks as CI
94+
check: lint check-format check-compat typecheck security check-licenses ## Run all checks
8995

9096
clean: ## Clean up build artifacts and caches
9197
find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true

libs/giskard-agents/pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ build-backend = "hatchling.build"
3333
[tool.pytest.ini_options]
3434
asyncio_mode = "auto"
3535
testpaths = ["tests"]
36+
markers = [
37+
"functional: marks tests that depend on external APIs (e.g., Gemini, OpenAI)",
38+
]
3639

3740
[tool.ruff.lint.isort.sections]
3841
"my_packages" = ["giskard"]

libs/giskard-agents/tests/test_embedding_model.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ async def test_litellm_embedding_model_embed_with_mock(
4949
assert np.isclose(embeddings[1], np.array([0.4, 0.5, 0.6])).all()
5050

5151

52+
@pytest.mark.functional
5253
async def test_embedding_model_real_embedding(
5354
embedding_model: LitellmEmbeddingModel,
5455
) -> None:

libs/giskard-agents/tests/test_generator.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ async def test_litellm_generator_completion_with_mock(
3939
assert response.finish_reason == "stop"
4040

4141

42+
@pytest.mark.functional
4243
async def test_generator_completion(generator: LiteLLMGenerator):
4344
response = await generator.complete(
4445
messages=[
@@ -57,6 +58,7 @@ async def test_generator_completion(generator: LiteLLMGenerator):
5758
assert response.finish_reason == "stop"
5859

5960

61+
@pytest.mark.functional
6062
async def test_generator_chat(generator: LiteLLMGenerator):
6163
test_message = "Hello, world!"
6264
pipeline = generator.chat(test_message)

libs/giskard-agents/tests/test_tools.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ async def get_weather(self, city: str) -> str:
8080
assert await weather.get_weather(city="Tokyo") == "It's sunny in Tokyo."
8181

8282

83+
@pytest.mark.functional
8384
async def test_tool_run(generator):
8485
"""Test that the tool runs correctly."""
8586

libs/giskard-agents/tests/test_workflow.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from pathlib import Path
22

3+
import pytest
34
from giskard import agents
45
from giskard.agents.chat import Chat
56
from giskard.agents.generators.litellm_generator import LiteLLMGenerator
67
from giskard.agents.templates.prompts_manager import PromptsManager
78
from pydantic import BaseModel
89

910

11+
@pytest.mark.functional
1012
async def test_single_run(generator):
1113
workflow = agents.ChatWorkflow(generator=generator)
1214

@@ -20,6 +22,7 @@ async def test_single_run(generator):
2022
assert "testbot" in chat.last.content.lower()
2123

2224

25+
@pytest.mark.functional
2326
async def test_run_many(generator):
2427
"""Test that the workflow runs correctly."""
2528

@@ -30,6 +33,7 @@ async def test_run_many(generator):
3033
assert len(chats) == 3
3134

3235

36+
@pytest.mark.functional
3337
async def test_run_batch(generator):
3438
"""Test that the workflow runs correctly."""
3539

@@ -50,6 +54,7 @@ async def test_run_batch(generator):
5054
assert len(chats) == 3
5155

5256

57+
@pytest.mark.functional
5358
async def test_stream_many(generator):
5459
workflow = agents.ChatWorkflow(generator=generator).chat("Hello!", role="user")
5560

@@ -61,6 +66,7 @@ async def test_stream_many(generator):
6166
assert len(chats) == 3
6267

6368

69+
@pytest.mark.functional
6470
async def test_stream_batch(generator):
6571
workflow = agents.ChatWorkflow(generator=generator).chat("Hello!", role="user")
6672

@@ -77,6 +83,7 @@ async def test_stream_batch(generator):
7783
assert len(chats) == 2
7884

7985

86+
@pytest.mark.functional
8087
async def test_workflow_with_mixed_templates(generator: LiteLLMGenerator):
8188
workflow = agents.ChatWorkflow(
8289
generator=generator,
@@ -123,6 +130,7 @@ async def test_workflow_with_mixed_templates(generator: LiteLLMGenerator):
123130
assert chat.messages[4].role == "assistant"
124131

125132

133+
@pytest.mark.functional
126134
async def test_output_format(generator):
127135
workflow = agents.ChatWorkflow(generator=generator)
128136

libs/giskard-checks/tests/conftest.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,10 @@ def pytest_collection_modifyitems(
2424
for item in items:
2525
if "integration" in item.keywords:
2626
item.add_marker(skip_integration)
27+
28+
29+
def pytest_sessionfinish(session, exitstatus):
30+
# If no tests were collected, set the exit status to 0 to avoid failure.
31+
# This is a workaround for packages not having any functional tests.
32+
if exitstatus == 5:
33+
session.exitstatus = 0
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def pytest_sessionfinish(session, exitstatus):
2+
# If no tests were collected, set the exit status to 0 to avoid failure.
3+
# This is a workaround for packages not having any functional tests.
4+
if exitstatus == 5:
5+
session.exitstatus = 0

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ giskard-core = { workspace = true }
4646
giskard-agents = { workspace = true }
4747
giskard-checks = { workspace = true }
4848

49+
[tool.pytest.ini_options]
50+
markers = [
51+
"functional: tests that require external API calls (deselect with '-m \"not functional\"')",
52+
]
53+
4954
[dependency-groups]
5055
dev = [
5156
"ipykernel==7.1.0",

0 commit comments

Comments
 (0)