1010 branches :
1111 - " *"
1212
13+ permissions : {}
14+
1315concurrency :
1416 group : ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
15- cancel-in-progress : false
17+ cancel-in-progress : true
1618
1719jobs :
1820 linting :
19- runs-on : ubuntu-latest
21+ runs-on : blacksmith-2vcpu- ubuntu-2404
2022 steps :
21- - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
23+ - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
24+ with :
25+ persist-credentials : false
2226 - name : Install uv and set Python version
23- uses : astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8
27+ uses : astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
2428 with :
2529 version : " 0.11.2"
2630 python-version : " 3.13"
27- enable-cache : true
31+ enable-cache : true # zizmor: ignore[cache-poisoning] CI-only, no artifacts published
2832 - name : Install dependencies
2933 run : uv sync --locked
3034 - name : Run Ruff
3135 run : uv run --frozen ruff check .
3236
3337 type-checking :
34- runs-on : ubuntu-latest
38+ runs-on : blacksmith-2vcpu- ubuntu-2404
3539 steps :
36- - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
40+ - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
41+ with :
42+ persist-credentials : false
3743 - name : Install uv and set Python version
38- uses : astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8
44+ uses : astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
3945 with :
4046 version : " 0.11.2"
4147 python-version : " 3.13"
42- enable-cache : true
43- - uses : actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
48+ enable-cache : true # zizmor: ignore[cache-poisoning] CI-only, no artifacts published
49+ - uses : actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 # zizmor: ignore[cache-poisoning]
4450 name : Cache mypy cache
4551 with :
4652 path : ./.mypy_cache
@@ -52,17 +58,14 @@ jobs:
5258 - name : Run mypy type checking
5359 run : uv run --frozen mypy langfuse --no-error-summary
5460
55- ci :
56- runs-on : ubuntu-latest
61+ unit-tests :
62+ runs-on : blacksmith-4vcpu- ubuntu-2404
5763 timeout-minutes : 30
5864 env :
5965 LANGFUSE_BASE_URL : " http://localhost:3000"
60- LANGFUSE_PUBLIC_KEY : " pk-lf-1234567890"
61- LANGFUSE_SECRET_KEY : " sk-lf-1234567890"
62- OPENAI_API_KEY : ${{ secrets.OPENAI_API_KEY }}
63- # SERPAPI_API_KEY: ${{ secrets.SERPAPI_API_KEY }}
64- HUGGINGFACEHUB_API_TOKEN : ${{ secrets.HUGGINGFACEHUB_API_TOKEN }}
65- ANTHROPIC_API_KEY : ${{ secrets.ANTHROPIC_API_KEY }}
66+ LANGFUSE_PUBLIC_KEY : " pk-lf-test"
67+ LANGFUSE_SECRET_KEY : " sk-lf-test"
68+ OPENAI_API_KEY : " test-openai-key"
6669 strategy :
6770 fail-fast : false
6871 matrix :
@@ -73,56 +76,99 @@ jobs:
7376 - " 3.13"
7477 - " 3.14"
7578
76- name : Test on Python version ${{ matrix.python-version }}
79+ name : Unit tests on Python ${{ matrix.python-version }}
7780 steps :
78- - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
79- - uses : pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
81+ - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
82+ with :
83+ persist-credentials : false
84+ - name : Install uv and set Python version
85+ uses : astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
8086 with :
81- version : 10.33.0
87+ version : " 0.11.2"
88+ python-version : ${{ matrix.python-version }}
89+ enable-cache : true # zizmor: ignore[cache-poisoning] CI-only, no artifacts published
8290
83- - name : Clone langfuse server
91+ - name : Check Python version
92+ run : python --version
93+
94+ - name : Install the project dependencies
95+ run : uv sync --locked
96+
97+ - name : Run the automated tests
8498 run : |
85- git clone https://github.com/langfuse/langfuse.git ./langfuse-server && echo $(cd ./langfuse-server && git rev-parse HEAD)
99+ python --version
100+ uv run --frozen pytest -n auto --dist worksteal -s -v --log-cli-level=INFO tests/unit
86101
87- - name : Setup node (for langfuse server)
88- uses : actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
89- with :
90- node-version : 24
102+ e2e-tests :
103+ runs-on : ubuntu-latest
104+ timeout-minutes : 30
105+ strategy :
106+ fail-fast : false
107+ matrix :
108+ include :
109+ - suite : e2e
110+ job_name : E2E shard 1 tests on Python 3.13
111+ shard_name : shard-1
112+ shard_index : 0
113+ shard_count : 2
114+ - suite : e2e
115+ job_name : E2E shard 2 tests on Python 3.13
116+ shard_name : shard-2
117+ shard_index : 1
118+ shard_count : 2
119+ - suite : live_provider
120+ job_name : E2E live-provider tests on Python 3.13
121+ shard_name : live-provider
122+ env :
123+ LANGFUSE_BASE_URL : " http://localhost:3000"
124+ LANGFUSE_PUBLIC_KEY : " pk-lf-1234567890"
125+ LANGFUSE_SECRET_KEY : " sk-lf-1234567890"
126+ LANGFUSE_INIT_ORG_ID : " 0c6c96f4-0ca0-4f16-92a8-6dd7d7c6a501"
127+ LANGFUSE_INIT_ORG_NAME : " SDK Test Org"
128+ LANGFUSE_INIT_PROJECT_ID : " 7a88fb47-b4e2-43b8-a06c-a5ce950dc53a"
129+ LANGFUSE_INIT_PROJECT_NAME : " SDK Test Project"
130+ LANGFUSE_INIT_PROJECT_PUBLIC_KEY : " pk-lf-1234567890"
131+ LANGFUSE_INIT_PROJECT_SECRET_KEY : " sk-lf-1234567890"
132+ LANGFUSE_INIT_USER_EMAIL : " sdk-tests@langfuse.local"
133+ LANGFUSE_INIT_USER_NAME : " SDK Tests"
134+ LANGFUSE_INIT_USER_PASSWORD : " langfuse-ci-password"
135+ LANGFUSE_E2E_READ_TIMEOUT_SECONDS : " 60"
136+ LANGFUSE_E2E_READ_INTERVAL_SECONDS : " 0.5"
137+ OPENAI_API_KEY : ${{ secrets.OPENAI_API_KEY }}
138+ # SERPAPI_API_KEY: ${{ secrets.SERPAPI_API_KEY }}
139+ HUGGINGFACEHUB_API_TOKEN : ${{ secrets.HUGGINGFACEHUB_API_TOKEN }}
140+ ANTHROPIC_API_KEY : ${{ secrets.ANTHROPIC_API_KEY }}
91141
92- - name : Cache langfuse server dependencies
93- uses : actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
142+ name : ${{ matrix.job_name }}
143+ steps :
144+ - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
94145 with :
95- path : ./langfuse-server/node_modules
96- key : |
97- langfuse-server-${{ hashFiles('./langfuse-server/package-lock.json') }}
98- langfuse-server-
146+ persist-credentials : false
147+ - name : Install uv and set Python version
148+ uses : astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
149+ with :
150+ version : " 0.11.2"
151+ python-version : " 3.13"
152+ enable-cache : true # zizmor: ignore[cache-poisoning] CI-only, no artifacts published
153+ - name : Install the project dependencies
154+ run : uv sync --locked
155+ - name : Check uv Python version
156+ run : uv run --frozen python --version
157+ - name : Prepare langfuse server compose
158+ run : |
159+ mkdir -p ./langfuse-server
160+ LANGFUSE_SERVER_SHA="$(git ls-remote https://github.com/langfuse/langfuse.git HEAD | cut -f1)"
161+ curl -fsSL "https://raw.githubusercontent.com/langfuse/langfuse/${LANGFUSE_SERVER_SHA}/docker-compose.yml" \
162+ -o ./langfuse-server/docker-compose.yml
163+ echo "${LANGFUSE_SERVER_SHA}"
99164
100165 - name : Run langfuse server
101166 run : |
102167 cd ./langfuse-server
103168
104- echo "::group::Run langfuse server"
105- TELEMETRY_ENABLED=false docker compose up -d postgres
106- echo "::endgroup::"
107-
108- echo "::group::Logs from langfuse server"
109- TELEMETRY_ENABLED=false docker compose logs
110- echo "::endgroup::"
111-
112- echo "::group::Install dependencies (necessary to run seeder)"
113- pnpm i
114- echo "::endgroup::"
115-
116- echo "::group::Seed db"
117- cp .env.dev.example .env
118- pnpm run db:migrate
119- pnpm run db:seed
120- echo "::endgroup::"
121- rm -rf .env
122-
123- echo "::group::Run server"
124-
169+ echo "::group::Start langfuse server"
125170 TELEMETRY_ENABLED=false \
171+ NEXT_PUBLIC_LANGFUSE_RUN_NEXT_INIT=true \
126172 LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT=http://localhost:9090 \
127173 LANGFUSE_INGESTION_QUEUE_DELAY_MS=10 \
128174 LANGFUSE_INGESTION_CLICKHOUSE_WRITE_INTERVAL_MS=10 \
@@ -131,51 +177,81 @@ jobs:
131177 LANGFUSE_ENABLE_EVENTS_TABLE_V2_APIS=true \
132178 LANGFUSE_ENABLE_EVENTS_TABLE_OBSERVATIONS=true \
133179 docker compose up -d
134-
135180 echo "::endgroup::"
136181
137- # Add this step to check the health of the container
138182 - name : Health check for langfuse server
139183 run : |
140184 echo "Checking if the langfuse server is up..."
141185 retry_count=0
142- max_retries=10
143- until curl --output /dev/null --silent --head --fail http://localhost:3000/api/public/health
186+ max_retries=20
187+ until curl --output /dev/null --silent --head --fail http://localhost:3000/api/public/health && \
188+ uv run --frozen python -c "from langfuse import Langfuse; client = Langfuse(); project_id = client._get_project_id(); assert project_id == '7a88fb47-b4e2-43b8-a06c-a5ce950dc53a', project_id; print(project_id)"
144189 do
145190 retry_count=`expr $retry_count + 1`
146191 echo "Attempt $retry_count of $max_retries..."
147192 if [ $retry_count -ge $max_retries ]; then
148193 echo "Langfuse server did not respond in time. Printing logs..."
149- docker logs langfuse-server-langfuse-web-1
194+ (cd ./langfuse-server && docker compose ps)
195+ (cd ./langfuse-server && docker compose logs langfuse-web langfuse-worker)
150196 echo "Failing the step..."
151197 exit 1
152198 fi
153199 sleep 5
154200 done
155201 echo "Langfuse server is up and running!"
156202
157- - name : Install uv and set Python version
158- uses : astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8
159- with :
160- version : " 0.11.2"
161- python-version : ${{ matrix.python-version }}
162- enable-cache : true
203+ - name : Select e2e shard files
204+ if : ${{ matrix.suite == 'e2e' }}
205+ run : |
206+ uv run --frozen python scripts/select_e2e_shard.py \
207+ --shard-index ${{ matrix.shard_index }} \
208+ --shard-count ${{ matrix.shard_count }} \
209+ --json
210+ uv run --frozen python scripts/select_e2e_shard.py \
211+ --shard-index ${{ matrix.shard_index }} \
212+ --shard-count ${{ matrix.shard_count }} \
213+ > "$RUNNER_TEMP/e2e-shard-files.txt"
214+ cat "$RUNNER_TEMP/e2e-shard-files.txt"
163215
164- - name : Check Python version
165- run : python --version
216+ - name : Run the parallel end-to-end tests
217+ if : ${{ matrix.suite == 'e2e' }}
218+ run : |
219+ uv run --frozen python --version
220+ mapfile -t e2e_files < "$RUNNER_TEMP/e2e-shard-files.txt"
221+ set +e
222+ uv run --frozen pytest -n 4 --dist worksteal -s -v --log-cli-level=INFO "${e2e_files[@]}" -m "not serial_e2e"
223+ status=$?
224+ set -e
225+ if [ "$status" -eq 5 ]; then
226+ echo "No parallel e2e tests selected for this shard."
227+ elif [ "$status" -ne 0 ]; then
228+ exit "$status"
229+ fi
166230
167- - name : Install the project dependencies
168- run : uv sync --locked
231+ - name : Run serial end-to-end tests
232+ if : ${{ matrix.suite == 'e2e' }}
233+ run : |
234+ mapfile -t e2e_files < "$RUNNER_TEMP/e2e-shard-files.txt"
235+ set +e
236+ uv run --frozen pytest -s -v --log-cli-level=INFO "${e2e_files[@]}" -m "serial_e2e"
237+ status=$?
238+ set -e
239+ if [ "$status" -eq 5 ]; then
240+ echo "No serial e2e tests selected for this shard."
241+ elif [ "$status" -ne 0 ]; then
242+ exit "$status"
243+ fi
169244
170- - name : Run the automated tests
245+ - name : Run live-provider tests
246+ if : ${{ matrix.suite == 'live_provider' }}
171247 run : |
172- python --version
173- uv run --frozen pytest -n auto --dist loadfile -s -v --log-cli-level=INFO
248+ uv run --frozen python --version
249+ uv run --frozen pytest -n 4 --dist worksteal -s -v --log-cli-level=INFO tests/live_provider -m "live_provider"
174250
175251 all-tests-passed :
176252 # This allows us to have a branch protection rule for tests and deploys with matrix
177- runs-on : ubuntu-latest
178- needs : [ci , linting, type-checking]
253+ runs-on : blacksmith-2vcpu- ubuntu-2404
254+ needs : [unit-tests, e2e-tests , linting, type-checking]
179255 if : always()
180256 steps :
181257 - name : Successful deploy
0 commit comments