lkr-dev-cli 0.0.30__tar.gz → 0.0.31__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. lkr_dev_cli-0.0.31/.github/workflows/test-dependencies.yml +34 -0
  2. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/Dockerfile +1 -1
  3. lkr_dev_cli-0.0.31/Makefile +7 -0
  4. lkr_dev_cli-0.0.30/README.md → lkr_dev_cli-0.0.31/PKG-INFO +117 -6
  5. lkr_dev_cli-0.0.30/PKG-INFO → lkr_dev_cli-0.0.31/README.md +86 -28
  6. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/tools/classes.py +5 -4
  7. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/tools/main.py +1 -1
  8. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/pyproject.toml +18 -3
  9. lkr_dev_cli-0.0.31/tests/TESTING.md +96 -0
  10. lkr_dev_cli-0.0.31/tests/test_dependency_resolution.py +180 -0
  11. lkr_dev_cli-0.0.31/tests/test_deps.sh +119 -0
  12. lkr_dev_cli-0.0.31/uv.lock +954 -0
  13. lkr_dev_cli-0.0.30/Makefile +0 -4
  14. lkr_dev_cli-0.0.30/uv.lock +0 -842
  15. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/.github/workflows/release.yml +0 -0
  16. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/.gitignore +0 -0
  17. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/.python-version +0 -0
  18. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/.vscode/launch.json +0 -0
  19. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/.vscode/settings.json +0 -0
  20. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/LICENSE +0 -0
  21. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/cloudbuild.yaml +0 -0
  22. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/__init__.py +0 -0
  23. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/auth/__init__.py +0 -0
  24. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/auth/main.py +0 -0
  25. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/auth/oauth.py +0 -0
  26. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/auth_service.py +0 -0
  27. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/classes.py +0 -0
  28. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/constants.py +0 -0
  29. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/custom_types.py +0 -0
  30. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/exceptions.py +0 -0
  31. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/logger.py +0 -0
  32. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/main.py +0 -0
  33. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/mcp/classes.py +0 -0
  34. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/mcp/main.py +0 -0
  35. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/mcp/utils.py +0 -0
  36. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/observability/classes.py +0 -0
  37. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/observability/embed_container.html +0 -0
  38. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/observability/main.py +0 -0
  39. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr/observability/utils.py +0 -0
  40. {lkr_dev_cli-0.0.30 → lkr_dev_cli-0.0.31}/lkr.md +0 -0
@@ -0,0 +1,34 @@
1
+ name: Test Dependency Resolution
2
+
3
+ on:
4
+ push:
5
+ branches: [ main, develop ]
6
+ pull_request:
7
+ branches: [ main, develop ]
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ test-dependencies:
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - name: Checkout code
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Setup Python
19
+ uses: actions/setup-python@v4
20
+ with:
21
+ python-version: '3.12'
22
+
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v2
25
+ with:
26
+ version: latest
27
+
28
+ - name: Install dependencies
29
+ run: |
30
+ uv sync --extra all
31
+
32
+ - name: Run with pytest
33
+ run: |
34
+ uv run pytest tests/test_dependency_resolution.py -v
@@ -9,6 +9,6 @@ WORKDIR /app
9
9
  COPY pyproject.toml uv.lock README.md LICENSE ./
10
10
  COPY lkr ./lkr
11
11
  ENV UV_PROJECT_ENVIRONMENT="/usr/local/"
12
- RUN uv sync --frozen --no-dev --no-editable
12
+ RUN uv sync --frozen --no-dev --no-editable --extra=all
13
13
 
14
14
  CMD []
@@ -0,0 +1,7 @@
1
+ .PHONY: docs test-deps
2
+
3
+ docs:
4
+ typer lkr/main.py utils docs --output lkr.md
5
+
6
+ test-deps:
7
+ python tests/test_dependency_resolution.py
@@ -1,3 +1,34 @@
1
+ Metadata-Version: 2.4
2
+ Name: lkr-dev-cli
3
+ Version: 0.0.31
4
+ Summary: lkr: a command line interface for looker
5
+ Author: bwebs
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.12
9
+ Requires-Dist: cryptography>=42.0.0
10
+ Requires-Dist: looker-sdk>=25.4.0
11
+ Requires-Dist: pydantic>=2.11.4
12
+ Requires-Dist: pydash>=8.0.5
13
+ Requires-Dist: questionary>=2.1.0
14
+ Requires-Dist: requests>=2.31.0
15
+ Requires-Dist: structlog>=25.3.0
16
+ Requires-Dist: typer>=0.15.2
17
+ Provides-Extra: all
18
+ Requires-Dist: duckdb>=1.2.2; extra == 'all'
19
+ Requires-Dist: fastapi>=0.115.12; extra == 'all'
20
+ Requires-Dist: mcp[cli]>=1.9.2; extra == 'all'
21
+ Requires-Dist: selenium>=4.32.0; extra == 'all'
22
+ Provides-Extra: embed-observability
23
+ Requires-Dist: fastapi>=0.115.12; extra == 'embed-observability'
24
+ Requires-Dist: selenium>=4.32.0; extra == 'embed-observability'
25
+ Provides-Extra: mcp
26
+ Requires-Dist: duckdb>=1.2.2; extra == 'mcp'
27
+ Requires-Dist: mcp[cli]>=1.9.2; extra == 'mcp'
28
+ Provides-Extra: user-attribute-updater
29
+ Requires-Dist: fastapi>=0.115.12; extra == 'user-attribute-updater'
30
+ Description-Content-Type: text/markdown
31
+
1
32
  # lkr cli
2
33
 
3
34
  The `lkr` cli is a tool for interacting with Looker. It combines Looker's SDK and customer logic to interact with Looker in meaninful ways. For a full list of commands, see the full [cli docs](./lkr.md)
@@ -192,10 +223,13 @@ This can also be used to stress test your Looker environment as it serves an API
192
223
  ## User Attribute Updater (OIDC Token)
193
224
 
194
225
  1. Create a new cloud run using the `lkr-cli` public docker image `us-central1-docker.pkg.dev/lkr-dev-production/lkr-cli/cli:latest`
195
- 2. Put in the environment variables LOOKERSDK_CLIENT_ID, LOOKERSDK_CLIENT_SECRET, LOOKERSDK_BASE_URL, LOOKER_WHITELISTED_BASE_URLS. The `LOOKER_WHITELISTED_BASE_URLS` would be the same url as the `LOOKERSDK_BASE_URL` if you are only using this for a single Looker instance. For more advanced use cases, you can set the `LOOKER_WHITELISTED_BASE_URLS` to a comma separated list of urls. The body of the request also accepts a `base_url`, `client_id`, and `client_secret` key that will override these settings.
196
- 3. Deploy the cloud run
197
- 4. Retrieve the URL of the cloud run
198
- 5. Create the user attribute
226
+ 2. Put in the environment variables LOOKERSDK_CLIENT_ID, LOOKERSDK_CLIENT_SECRET, LOOKERSDK_BASE_URL, LOOKER_WHITELISTED_BASE_URLS. The `LOOKER_WHITELISTED_BASE_URLS` would be the same url as the `LOOKERSDK_BASE_URL` if you are only using this for a single Looker instance. For more advanced use cases, you can set the `LOOKER_WHITELISTED_BASE_URLS` to a comma separated list of urls. The body of the request also accepts a `base_url`, `client_id`, and `client_secret` key that will override these settings. See example [`gcloud` command](#example-gcloud-command)
227
+ 3. For the command and arguments use:
228
+ - command: `lkr`
229
+ - args: `tools` `user-attribute-updater`
230
+ 4. Deploy the cloud run
231
+ 5. Retrieve the URL of the cloud run
232
+ 6. Create the user attribute
199
233
  - Name: cloud_run_access_token
200
234
  - Data Type: String
201
235
  - User Access: None
@@ -228,6 +262,23 @@ This can also be used to stress test your Looker environment as it serves an API
228
262
  9. Check the logs of the cloud run to see if there was a 200 response
229
263
 
230
264
 
265
+ ### Example `gcloud` command
266
+ ```bash
267
+ export REGION=<your region>
268
+ export PROJECT=<your project id>
269
+
270
+ gcloud run deploy lkr-access-token-updater \
271
+ --image us-central1-docker.pkg.dev/lkr-dev-production/lkr-cli/cli:latest \
272
+ --command lkr \
273
+ --args tools,user-attribute-updater \
274
+ --platform managed \
275
+ --region $REGION \
276
+ --project $PROJECT \
277
+ --cpu 1 \
278
+ --memory 2Gi \
279
+ --set-env-vars LOOKERSDK_CLIENT_ID=<your client id>,LOOKERSDK_CLIENT_SECRET=<your client secret>,LOOKERSDK_BASE_URL=<your instance url>,LOOKER_WHITELISTED_BASE_URLS=<your instance url>
280
+ ```
281
+
231
282
  ## UserAttributeUpdater `lkr-dev-cli`
232
283
 
233
284
  Exported from the `lkr-dev-cli` package is the `UserAttributeUpdater` pydantic class. This class has all the necessary logic to update a user attribute value.
@@ -279,12 +330,12 @@ from lkr import UserAttributeUpdater
279
330
  def request_authorization(request: Request):
280
331
  body = await request.json()
281
332
  updater = UserAttributeUpdater.model_validate(body)
282
- updater.get_request_authorization_for_value(request)
333
+ updater.get_request_authorization_for_value(request.headers.items())
283
334
  updater.update_user_attribute_value()
284
335
 
285
336
  @app.post("/as_body")
286
337
  def as_body(request: Request, body: UserAttributeUpdater):
287
- body.get_request_authorization_for_value(request)
338
+ body.get_request_authorization_for_value(request.headers.items())
288
339
  body.update_user_attribute_value()
289
340
 
290
341
  @app.post("/assigning_value")
@@ -295,4 +346,64 @@ def assigning_value(request: Request):
295
346
  )
296
347
  updater.value = request.headers.get("my_custom_header")
297
348
  updater.update_user_attribute_value()
349
+
350
+ @app.delete("/:user_attribute_name/:email")
351
+ def delete_user_attribute(user_attribute_name: str, email: str):
352
+ updater = UserAttributeUpdater(
353
+ user_attribute=user_attribute_name,
354
+ update_type="user",
355
+ email=email,
356
+ )
357
+ updater.delete_user_attribute_value()
358
+
359
+ ## Optional Dependencies
360
+
361
+ The `lkr` CLI supports optional dependencies that enable additional functionality. You can install these individually or all at once.
362
+
363
+ ### Available Extras
364
+
365
+ - **`mcp`**: Enables the MCP (Model Context Protocol) server functionality
366
+ - Includes: `mcp[cli]>=1.9.2`, `duckdb>=1.2.2`
367
+ - **`embed-observability`**: Enables the observability embed monitoring features
368
+ - Includes: `fastapi>=0.115.12`, `selenium>=4.32.0`
369
+ - **`user-attribute-updater`**: Enables the user attribute updater functionality
370
+ - Includes: `fastapi>=0.115.12`
371
+
372
+ ### Installing Optional Dependencies
373
+
374
+ **Install all optional dependencies:**
375
+ ```bash
376
+ uv sync --extra all
298
377
  ```
378
+
379
+ **Install specific extras:**
380
+ ```bash
381
+ # Install MCP functionality
382
+ uv sync --extra mcp
383
+
384
+ # Install observability features
385
+ uv sync --extra embed-observability
386
+
387
+ # Install user attribute updater
388
+ uv sync --extra user-attribute-updater
389
+
390
+ # Install multiple extras
391
+ uv sync --extra mcp --extra embed-observability
392
+ ```
393
+
394
+ **Using pip:**
395
+ ```bash
396
+ # Install all optional dependencies
397
+ pip install lkr-dev-cli[all]
398
+
399
+ # Install specific extras
400
+ pip install lkr-dev-cli[mcp,embed-observability,user-attribute-updater]
401
+ ```
402
+
403
+ ### What Each Extra Enables
404
+
405
+ - **`mcp`**: Use the MCP server with tools like Cursor for enhanced IDE integration
406
+ - **`embed-observability`**: Run the observability embed server for monitoring Looker dashboard performance
407
+ - **`user-attribute-updater`**: Deploy the user attribute updater service for OIDC token management
408
+
409
+ All extras are designed to work together seamlessly, and installing `all` is equivalent to installing all individual extras.
@@ -1,25 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: lkr-dev-cli
3
- Version: 0.0.30
4
- Summary: lkr: a command line interface for looker
5
- Author: bwebs
6
- License-Expression: MIT
7
- License-File: LICENSE
8
- Requires-Python: >=3.12
9
- Requires-Dist: cryptography>=42.0.0
10
- Requires-Dist: duckdb>=1.2.2
11
- Requires-Dist: fastapi>=0.115.12
12
- Requires-Dist: looker-sdk>=25.4.0
13
- Requires-Dist: mcp[cli]>=1.9.2
14
- Requires-Dist: pydantic>=2.11.4
15
- Requires-Dist: pydash>=8.0.5
16
- Requires-Dist: questionary>=2.1.0
17
- Requires-Dist: requests>=2.31.0
18
- Requires-Dist: selenium>=4.32.0
19
- Requires-Dist: structlog>=25.3.0
20
- Requires-Dist: typer>=0.15.2
21
- Description-Content-Type: text/markdown
22
-
23
1
  # lkr cli
24
2
 
25
3
  The `lkr` cli is a tool for interacting with Looker. It combines Looker's SDK and customer logic to interact with Looker in meaninful ways. For a full list of commands, see the full [cli docs](./lkr.md)
@@ -214,10 +192,13 @@ This can also be used to stress test your Looker environment as it serves an API
214
192
  ## User Attribute Updater (OIDC Token)
215
193
 
216
194
  1. Create a new cloud run using the `lkr-cli` public docker image `us-central1-docker.pkg.dev/lkr-dev-production/lkr-cli/cli:latest`
217
- 2. Put in the environment variables LOOKERSDK_CLIENT_ID, LOOKERSDK_CLIENT_SECRET, LOOKERSDK_BASE_URL, LOOKER_WHITELISTED_BASE_URLS. The `LOOKER_WHITELISTED_BASE_URLS` would be the same url as the `LOOKERSDK_BASE_URL` if you are only using this for a single Looker instance. For more advanced use cases, you can set the `LOOKER_WHITELISTED_BASE_URLS` to a comma separated list of urls. The body of the request also accepts a `base_url`, `client_id`, and `client_secret` key that will override these settings.
218
- 3. Deploy the cloud run
219
- 4. Retrieve the URL of the cloud run
220
- 5. Create the user attribute
195
+ 2. Put in the environment variables LOOKERSDK_CLIENT_ID, LOOKERSDK_CLIENT_SECRET, LOOKERSDK_BASE_URL, LOOKER_WHITELISTED_BASE_URLS. The `LOOKER_WHITELISTED_BASE_URLS` would be the same url as the `LOOKERSDK_BASE_URL` if you are only using this for a single Looker instance. For more advanced use cases, you can set the `LOOKER_WHITELISTED_BASE_URLS` to a comma separated list of urls. The body of the request also accepts a `base_url`, `client_id`, and `client_secret` key that will override these settings. See example [`gcloud` command](#example-gcloud-command)
196
+ 3. For the command and arguments use:
197
+ - command: `lkr`
198
+ - args: `tools` `user-attribute-updater`
199
+ 4. Deploy the cloud run
200
+ 5. Retrieve the URL of the cloud run
201
+ 6. Create the user attribute
221
202
  - Name: cloud_run_access_token
222
203
  - Data Type: String
223
204
  - User Access: None
@@ -250,6 +231,23 @@ This can also be used to stress test your Looker environment as it serves an API
250
231
  9. Check the logs of the cloud run to see if there was a 200 response
251
232
 
252
233
 
234
+ ### Example `gcloud` command
235
+ ```bash
236
+ export REGION=<your region>
237
+ export PROJECT=<your project id>
238
+
239
+ gcloud run deploy lkr-access-token-updater \
240
+ --image us-central1-docker.pkg.dev/lkr-dev-production/lkr-cli/cli:latest \
241
+ --command lkr \
242
+ --args tools,user-attribute-updater \
243
+ --platform managed \
244
+ --region $REGION \
245
+ --project $PROJECT \
246
+ --cpu 1 \
247
+ --memory 2Gi \
248
+ --set-env-vars LOOKERSDK_CLIENT_ID=<your client id>,LOOKERSDK_CLIENT_SECRET=<your client secret>,LOOKERSDK_BASE_URL=<your instance url>,LOOKER_WHITELISTED_BASE_URLS=<your instance url>
249
+ ```
250
+
253
251
  ## UserAttributeUpdater `lkr-dev-cli`
254
252
 
255
253
  Exported from the `lkr-dev-cli` package is the `UserAttributeUpdater` pydantic class. This class has all the necessary logic to update a user attribute value.
@@ -301,12 +299,12 @@ from lkr import UserAttributeUpdater
301
299
  def request_authorization(request: Request):
302
300
  body = await request.json()
303
301
  updater = UserAttributeUpdater.model_validate(body)
304
- updater.get_request_authorization_for_value(request)
302
+ updater.get_request_authorization_for_value(request.headers.items())
305
303
  updater.update_user_attribute_value()
306
304
 
307
305
  @app.post("/as_body")
308
306
  def as_body(request: Request, body: UserAttributeUpdater):
309
- body.get_request_authorization_for_value(request)
307
+ body.get_request_authorization_for_value(request.headers.items())
310
308
  body.update_user_attribute_value()
311
309
 
312
310
  @app.post("/assigning_value")
@@ -317,4 +315,64 @@ def assigning_value(request: Request):
317
315
  )
318
316
  updater.value = request.headers.get("my_custom_header")
319
317
  updater.update_user_attribute_value()
318
+
319
+ @app.delete("/:user_attribute_name/:email")
320
+ def delete_user_attribute(user_attribute_name: str, email: str):
321
+ updater = UserAttributeUpdater(
322
+ user_attribute=user_attribute_name,
323
+ update_type="user",
324
+ email=email,
325
+ )
326
+ updater.delete_user_attribute_value()
327
+
328
+ ## Optional Dependencies
329
+
330
+ The `lkr` CLI supports optional dependencies that enable additional functionality. You can install these individually or all at once.
331
+
332
+ ### Available Extras
333
+
334
+ - **`mcp`**: Enables the MCP (Model Context Protocol) server functionality
335
+ - Includes: `mcp[cli]>=1.9.2`, `duckdb>=1.2.2`
336
+ - **`embed-observability`**: Enables the observability embed monitoring features
337
+ - Includes: `fastapi>=0.115.12`, `selenium>=4.32.0`
338
+ - **`user-attribute-updater`**: Enables the user attribute updater functionality
339
+ - Includes: `fastapi>=0.115.12`
340
+
341
+ ### Installing Optional Dependencies
342
+
343
+ **Install all optional dependencies:**
344
+ ```bash
345
+ uv sync --extra all
320
346
  ```
347
+
348
+ **Install specific extras:**
349
+ ```bash
350
+ # Install MCP functionality
351
+ uv sync --extra mcp
352
+
353
+ # Install observability features
354
+ uv sync --extra embed-observability
355
+
356
+ # Install user attribute updater
357
+ uv sync --extra user-attribute-updater
358
+
359
+ # Install multiple extras
360
+ uv sync --extra mcp --extra embed-observability
361
+ ```
362
+
363
+ **Using pip:**
364
+ ```bash
365
+ # Install all optional dependencies
366
+ pip install lkr-dev-cli[all]
367
+
368
+ # Install specific extras
369
+ pip install lkr-dev-cli[mcp,embed-observability,user-attribute-updater]
370
+ ```
371
+
372
+ ### What Each Extra Enables
373
+
374
+ - **`mcp`**: Use the MCP server with tools like Cursor for enhanced IDE integration
375
+ - **`embed-observability`**: Run the observability embed server for monitoring Looker dashboard performance
376
+ - **`user-attribute-updater`**: Deploy the user attribute updater service for OIDC token management
377
+
378
+ All extras are designed to work together seamlessly, and installing `all` is equivalent to installing all individual extras.
@@ -1,6 +1,5 @@
1
1
  from typing import Literal, Optional, Self, cast
2
2
 
3
- from fastapi import Request
4
3
  from looker_sdk.sdk.api40.methods import Looker40SDK
5
4
  from looker_sdk.sdk.api40.models import (
6
5
  UserAttributeGroupValue,
@@ -46,10 +45,12 @@ class UserAttributeUpdater(BaseModel):
46
45
  )
47
46
  return self
48
47
 
49
- def get_request_authorization_for_value(self, request: Request):
50
- authorization_token = request.headers.get("Authorization")
48
+ def get_request_authorization_for_value(self, headers: list[tuple[str, str]]):
49
+ authorization_token = next(
50
+ (header for header in headers if header[0] == "Authorization"), None
51
+ )
51
52
  if authorization_token:
52
- self.value = authorization_token
53
+ self.value = authorization_token[1]
53
54
  else:
54
55
  logger.error("No authorization token found")
55
56
 
@@ -22,7 +22,7 @@ def user_attribute_updater(
22
22
  @api.post("/identity_token")
23
23
  def identity_token(request: Request, body: UserAttributeUpdater):
24
24
  try:
25
- body.get_request_authorization_for_value(request)
25
+ body.get_request_authorization_for_value(request.headers.items())
26
26
  body.update_user_attribute_value()
27
27
  raw_urls = os.getenv("LOOKER_WHITELISTED_BASE_URLS", "")
28
28
  whitelisted_base_urls = (
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lkr-dev-cli"
3
- version = "0.0.30"
3
+ version = "0.0.31"
4
4
  description = "lkr: a command line interface for looker"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -17,10 +17,25 @@ dependencies = [
17
17
  "pydash>=8.0.5",
18
18
  "structlog>=25.3.0",
19
19
  "questionary>=2.1.0",
20
- "duckdb>=1.2.2",
20
+ ]
21
+
22
+ [project.optional-dependencies]
23
+ mcp = [
24
+ "mcp[cli]>=1.9.2",
25
+ "duckdb>=1.2.2"
26
+ ]
27
+ embed-observability = [
21
28
  "fastapi>=0.115.12",
22
- "selenium>=4.32.0",
29
+ "selenium>=4.32.0"
30
+ ]
31
+ user-attribute-updater = [
32
+ "fastapi>=0.115.12"
33
+ ]
34
+ all = [
23
35
  "mcp[cli]>=1.9.2",
36
+ "fastapi>=0.115.12",
37
+ "selenium>=4.32.0",
38
+ "duckdb>=1.2.2"
24
39
  ]
25
40
 
26
41
  [project.scripts]
@@ -0,0 +1,96 @@
1
+ # Testing Dependency Resolution
2
+
3
+ This project includes tests to ensure that all dependency combinations resolve to the same lock file, maintaining consistency in the dependency tree.
4
+
5
+ ## What the tests verify
6
+
7
+ The tests ensure that these two commands produce identical lock files:
8
+
9
+ 1. `uv sync --extra all`
10
+ 2. `uv sync --extra [all individual extras]`
11
+
12
+ Where "all individual extras" are dynamically discovered from `pyproject.toml` (excluding the `all` extra itself).
13
+
14
+ This is important because the `all` extra should be equivalent to installing all individual extras together.
15
+
16
+ ## Running the tests
17
+
18
+ ### Option 1: Shell script (recommended for quick testing)
19
+
20
+ ```bash
21
+ ./tests/test_deps.sh
22
+ ```
23
+
24
+ This script will:
25
+ - Dynamically discover all individual extras from `pyproject.toml`
26
+ - Create temporary directories
27
+ - Run both dependency resolution commands
28
+ - Compare the resulting lock files
29
+ - Clean up automatically
30
+
31
+ ### Option 2: Python test (for CI/CD integration)
32
+
33
+ ```bash
34
+ # Run directly
35
+ python tests/test_dependency_resolution.py
36
+
37
+ # Run with pytest
38
+ uv run pytest tests/test_dependency_resolution.py -v
39
+ ```
40
+
41
+ ### Option 3: Makefile target
42
+
43
+ ```bash
44
+ make test-deps
45
+ ```
46
+
47
+ ## What the tests check
48
+
49
+ 1. **Dynamic Extra Discovery**: Automatically discovers all individual extras from `pyproject.toml`
50
+ 2. **Dependency Resolution Consistency**: Ensures that `--extra all` and `--extra [individual extras]` resolve to the same set of packages
51
+ 3. **Configuration Validation**: Verifies that all required extras are properly defined in `pyproject.toml`
52
+ 4. **Lock File Integrity**: Compares SHA256 hashes of generated lock files
53
+
54
+ ## Expected output
55
+
56
+ When the tests pass, you should see:
57
+
58
+ ```
59
+ 🔍 Discovering individual extras...
60
+ 📦 Found individual extras: mcp embed-observability user-attribute-updater
61
+ 🎉 SUCCESS: All dependency combinations resolve to the same lock file!
62
+ ✅ Dependency resolution is consistent
63
+ ```
64
+
65
+ ## Adding new extras
66
+
67
+ When you add a new extra to `pyproject.toml`, the tests will automatically:
68
+
69
+ 1. Discover the new extra
70
+ 2. Include it in the dependency resolution test
71
+ 3. Verify that `--extra all` still includes all dependencies from the new extra
72
+
73
+ No manual test updates are required!
74
+
75
+ ## Troubleshooting
76
+
77
+ If the tests fail, it usually means:
78
+
79
+ 1. **Missing dependencies**: The `all` extra doesn't include all the dependencies from individual extras
80
+ 2. **Version conflicts**: Different dependency combinations resolve to different versions
81
+ 3. **Configuration issues**: The `pyproject.toml` file has inconsistencies
82
+
83
+ ### Common fixes
84
+
85
+ 1. **Update the `all` extra**: Make sure it includes all dependencies from individual extras
86
+ 2. **Check for version conflicts**: Ensure that all extras use compatible versions
87
+ 3. **Verify dependency definitions**: Make sure all extras are properly defined
88
+
89
+ ## CI/CD Integration
90
+
91
+ The tests are automatically run in GitHub Actions on:
92
+ - Push to `main` or `develop` branches
93
+ - Pull requests to `main` or `develop` branches
94
+ - Manual workflow dispatch
95
+
96
+ See `.github/workflows/test-dependencies.yml` for the CI configuration.