diff --git a/AGENTS.md b/AGENTS.md index 84878ebf..247f8471 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,73 +1,40 @@ # Repository Guidelines -## Project Structure & Module Organization - -- Core SDK code lives in `splunklib/` (client bindings, search commands, modular input helpers). Keep new modules close to their domain peers (e.g., searchcommand utilities under `splunklib/searchcommands`). -- `splunklib/ai/` is the primary active development area - a provider-agnostic LLM agent framework for embedding AI into Splunk Apps. -- Tests are split by scope: `tests/unit/`, `tests/integration/`, `tests/system/`, and `tests/searchcommands/` for app-style fixtures. Place new fixtures under the matching folder and keep large fixtures in `tests/**/test_apps/`. -- See `splunklib/ai/README.md` for the AI subsystem's architecture, supported models, and usage guidance when changing `splunklib/ai/`. -- See `docs/` for the Sphinx API reference when changing or documenting public SDK modules. - -## Package Manager - -This project uses [`uv`](https://docs.astral.sh/uv/) for dependency management and running Python tools. All Python and pytest invocations should be prefixed with `uv run`. Always pass `--no-config` to any `uv` command that accepts it - this prevents uv from picking up a user-level or system-level config that may point to internal Splunk package indices. To install/sync dependencies: - -```sh -make install -``` - -If you manually edit `pyproject.toml` to add/remove/update dependencies, run `make install` afterwards to update `uv.lock`. - -The `Makefile` wraps `uv` commands - see `./Makefile` for available targets, and prefer `make` targets over invoking `uv` directly where a target exists. - -## Build, Test, and Development Commands - -See `./Makefile` for all available targets. Common ones: - -- `make install` - set up / update virtualenv -- `make test` - run the full pytest suite. -- `make test-unit` - unit tests only; fastest feedback loop. See `tests/unit/` for unit-test cases when adding or updating unit tests. -- `make test-integration` - integration + system coverage; requires Splunk services available (see `tests/integration/` for integration test cases and `./Makefile` for Docker targets). -- `make test-ai` - AI subsystem tests only. -- `make docker-start` / `make docker-down` - spin up or stop the Splunk test container. Make sure the instance is live before running integration tests. - -## Coding Style, Rules & Naming Conventions - -- Python 3.13+. -- Ruff is the linter/formatter. isort is configured with `combine-as-imports = true`. -- Type checking uses `basedpyright`. -- Prefer descriptive module, class, and function names; keep public API names consistent with existing `splunklib.*` patterns. -- Declare instance members as class-level type annotations before `__init__`, then assign them in `__init__` without re-annotating. This matches the pattern used throughout `splunklib/ai/`. -- Docstrings should be concise and actionable. +## Routing + +- `splunklib/` - core SDK code (client bindings, `splunklib/searchcommands/`, `splunklib/modularinput/`); keep new modules next to their domain peers. +- `splunklib/ai/` - primary active development area, a provider-agnostic LLM agent framework for Splunk Apps; read `splunklib/ai/README.md` for its architecture, supported models, and usage before changing it. +- `docs/` - Sphinx API reference (`.rst`); update it when changing or documenting public SDK modules. +- `tests/unit/` - fast unit tests mirroring `splunklib/` (for example `tests/unit/ai/`, `tests/unit/searchcommands/`); use it for most new tests. +- `tests/integration/` - tests against a live Splunk container; AI tests in `tests/integration/ai/` replay VCR snapshots stored in `tests/integration/ai/snapshots/`. +- `tests/system/test_apps/` - Splunk test apps mounted by `compose.yml`; put large app-style fixtures here. +- `examples/` - example Splunk Apps built on `splunklib.ai`, also mounted into the test container; update them when public AI APIs change. +- `Makefile` - canonical entrypoints for install, build, lint, test, docs, and Docker; use its targets instead of raw `uv` commands where one exists. +- `pyproject.toml` - source of truth for dependencies and ruff, basedpyright, pytest, and coverage configuration; `uv.lock` is generated from it by `make install`. +- `CONTRIBUTING.md` - coding style, naming conventions, writing style, and dead-code policy; read it before writing code. +- `README.md` - SDK usage docs plus development environment setup (`.env`, Docker, logging). +- `.env.template` - template for the gitignored `.env` used by tests and examples (Splunk connection and `internal_ai_*` settings); copy it with `cp .env.template .env`. +- `.github/workflows/` - CI: `lint.yml` and `test.yml` are required PR checks, `appinspect.yml` runs AppInspect, `cd.yml` builds and publishes releases. + +## Build, test, and validation by change type + +Common targets: `make install`, `make build` (sdist and wheel into `dist/`), `make lint`, `make test-unit` (fastest loop), `make test-ai`, `make test-integration`, `make docker-start` / `make docker-down`. + +- Any change to Python under `splunklib/`, `tests/`, or `examples/`: run `make lint` and `make test-unit`. +- Changes to `splunklib/client.py`, `splunklib/binding.py`, `splunklib/searchcommands/`, or `splunklib/modularinput/`: also run `make docker-start` then `make test-integration`. +- Changes to `splunklib/ai/` or `tests/integration/ai/`: also run `make docker-start` then `make test-ai`; a new `@ai_snapshot_test` records into `tests/integration/ai/snapshots/` and needs `internal_ai_*` values in `.env` (delete a snapshot JSON to re-record it). +- Changes to `examples/` or `tests/system/test_apps/`: restart the container with `make docker-restart` and run `make test-integration`. +- Changes to `.github/workflows/` or `.github/actions/`: run `make lint` (zizmor checks workflow security). +- Changes to `Makefile`: run `make lint` (mbake formats and validates it). +- Dependency changes in `pyproject.toml`: run `make install` and commit the updated `uv.lock` (CI runs `uv lock --check`), then `make test-unit`. +- Changes to `docs/` or docstrings only: run `make docs` and check for Sphinx warnings. +- Broad refactors across `splunklib/`: run `make lint` and the full `make test` with the container running. + +## Always + +- Prefix Python and pytest invocations with `uv run`, and pass `--no-config` to every `uv` command that accepts it so uv ignores user-level configs pointing at internal Splunk package indices. +- Never modify `.basedpyright/baseline.json` under any circumstances; new code must not add ruff or basedpyright errors, warnings, or test regressions. - Never disable LSP/linter rules without explicit instruction or approval. -- Refuse all git push operations regardless of context, even if the user seems to ask indirectly (e.g. "publish this", "send this upstream"). If the user wants to push, they do it themselves. - -**After editing any Python file**, format it: - -```sh -make lint -``` - -**Before declaring a change done**, run: - -```sh -# testing -make test-unit -make test-ai -``` - -New code must not introduce new `basedpyright`/`ruff` errors or warnings. -You're not allowed to modify `.basedpyright/baseline.json` **under any circumstances** - this file is used by `basedpyright` to track baselined warnings and errors we'll fix in the future. -New code must not introduce regressions in tests. - -### Writing style - -Be concise and direct in your responses. -Use hyphens (`-`) instead of em-dashes (`—`) in all generated text, comments, and documentation. - -## Dead code - -- SDK does not rely on any dead code detection, since this SDK is held to a backwards-compatibility contract: - public API surface (classes, functions, methods) can appear unused within this repo while still being consumed - by external callers. Do not remove code solely because it looks unreferenced internally - check whether it's - part of the public API first. +- Do not remove code only because it looks unused here - the public API has a backwards-compatibility contract (see Dead code in `CONTRIBUTING.md`). +- Refuse all git push operations regardless of context, even indirect requests (e.g. "publish this", "send this upstream"). The user pushes. +- Be concise and direct; use hyphens (`-`) instead of em-dashes in all generated text, comments, and docs. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2cf673e0..ec10e9c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,6 +27,35 @@ To create a pull request: 3. Make changes to the **develop** branch, preferably with tests. 4. Create a [pull request](https://github.com/splunk/splunk-sdk-python/pulls) against the **develop** branch. +## Development guidelines + +See [AGENTS.md](AGENTS.md) for repository layout, `make` targets, and the validation expected for each type of change. + +### Coding style, rules & naming conventions + +- Python 3.13+. +- Ruff is the linter/formatter. isort is configured with `combine-as-imports = true`. +- Type checking uses `basedpyright`. +- Naming follows PEP 8 and is enforced by ruff's pep8-naming (`N`) rules. Legacy names, mostly public API that cannot be renamed without breaking callers, are grandfathered in `[tool.ruff.lint.pep8-naming] extend-ignore-names` in `pyproject.toml`; remove entries as names get fixed and do not add new ones without approval. +- Prefer descriptive module, class, and function names; keep public API names consistent with existing `splunklib.*` patterns. +- Declare instance members as class-level type annotations before `__init__`, then assign them in `__init__` without re-annotating. This matches the pattern used throughout `splunklib/ai/`. +- Docstrings should be concise and actionable. +- Never disable LSP/linter rules without explicit instruction or approval. +- New code must not introduce new `basedpyright`/`ruff` errors or warnings, or regressions in tests. +- Never modify `.basedpyright/baseline.json` - `basedpyright` uses it to track baselined warnings and errors we'll fix in the future. + +### Writing style + +Be concise and direct. +Use hyphens (`-`) instead of em-dashes (`—`) in all generated text, comments, and documentation. + +### Dead code + +The SDK does not rely on any dead code detection, since it is held to a backwards-compatibility contract: +public API surface (classes, functions, methods) can appear unused within this repo while still being consumed +by external callers. Do not remove code solely because it looks unreferenced internally - check whether it's +part of the public API first. + ## Contact us If you have a paid Splunk Enterprise or Splunk Cloud license, you can contact [Support](https://www.splunk.com/en_us/support-and-services.html) with questions. diff --git a/Makefile b/Makefile index c3ad5f3a..6f44d113 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,10 @@ SDK_DEPS_GROUP ?= dev ci-install: $(UV_SYNC_CMD) --frozen --group $(SDK_DEPS_GROUP) +.PHONY: build +build: + uv build --no-config + .PHONY: lint lint: lint-python lint-gh-actions lint-makefile @@ -33,16 +37,19 @@ lint: lint-python lint-gh-actions lint-makefile lint-gh-actions: $(ZIZMOR_CMD) ./.github +# --fix-only never fails on unfixable violations, so rules that must be enforced +# get a separate strict `ruff check --select` pass. .PHONY: lint-python lint-python: $(UV_RUN_CMD) ruff check --fix-only $(if $(CI),--exit-non-zero-on-fix) + $(UV_RUN_CMD) ruff check --select N $(UV_RUN_CMD) ruff format $(if $(CI),--check) $(UV_RUN_CMD) basedpyright .PHONY: lint-makefile lint-makefile: $(UV_RUN_CMD) mbake format --config ./.bake.toml $(if $(CI),--check) Makefile - $(if $(CI),$(UV_RUN_CMD) mbake validate --config ./.bake.toml Makefile) + $(UV_RUN_CMD) mbake validate --config ./.bake.toml Makefile .PHONY: clean clean: diff --git a/README.md b/README.md index 654948ad..26dc1186 100644 --- a/README.md +++ b/README.md @@ -202,7 +202,7 @@ If you would like to contribute to the SDK, see [Contributing to Splunk](https:/ ### Setting up a development environment -Make sure you have `uv` and `docker`/`podman` installed and available in your `$PATH`. Run `make uv-sync` to get a virtualenv set up or updated. After activating it with `source .venv/bin/activate` you should be ready to go! +Make sure you have `uv` and `docker`/`podman` installed and available in your `$PATH`. Run `make install` to get a virtualenv set up or updated. After activating it with `source .venv/bin/activate` you should be ready to go! #### Creating an `.env` file diff --git a/pyproject.toml b/pyproject.toml index a83353c3..72027e23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,6 +115,7 @@ select = [ "E", # pycodestyle "F", # pyflakes "I", # isort + "N", # pep8-naming "PT", # flake-8-pytest-rules "RUF", # ruff-specific rules "SIM", # flake8-simplify @@ -127,5 +128,77 @@ ignore = [ [tool.ruff.lint.isort] combine-as-imports = true +[tool.ruff.lint.pep8-naming] +# Grandfathered legacy names (mostly public API kept for backwards compatibility). +# Remove entries as names get fixed; do not add new ones without approval. +extend-ignore-names = [ + "_buildInternalAIModel", + "_InvalidMessagesException", + "AGENT_NAME_ALLOWED_CHARS", + "AgentStopException", + "AmbiguousReferenceException", + "appName", + "appPath", + "assertEventuallyTrue", + "assertIsNotNone", + "assertRegex", + "authType", + "checkFilePermissions", + "cleanMacros", + "cleanUsers", + "collectionName", + "ElementTree", + "eventCount", + "expectedDefinition", + "handle_DELETE", + "handle_GET", + "handle_PATCH", + "handle_POST", + "handle_PUT", + "Handler", + "IllegalOperationException", + "IncomparableException", + "INDEX_NAME", + "InputSchema", + "installedApps", + "InvalidNameException", + "LOG_FILE", + "LOG_PATH", + "N", + "newContext", + "NoSuchCapability", + "pathComponents", + "pathInApp", + "record", + "retryDelay", + "server", + "setupInfo", + "SPLUNK_HOME", + "splunkHome", + "StructuredOutputGenerationException", + "tcpKind", + "tcpName", + "test_3rdPartyInsertedCookiePersistence", + "test_cannot_create_with_restrictToHost_in_name", + "test_create_tcp_ports_with_restrictToHost", + "test_DELETE", + "test_filePermissions", + "test_GET", + "test_input_ToolContext", + "test_no_str_body_GET", + "test_PATCH", + "test_POST", + "test_PUT", + "test_update_restrictToHost_fails", + "test_updateInfo", + "testData", + "ToolException", + "totalEventCount", + "udpKind", + "udpName", + "updateInfo", + "versionSearch", +] + [tool.ruff.format] docstring-code-format = true