netbox-super-cli 1.0.0__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.
- netbox_super_cli-1.0.0/.github/workflows/agents-md-sync.yml +35 -0
- netbox_super_cli-1.0.0/.github/workflows/bench.yml +23 -0
- netbox_super_cli-1.0.0/.github/workflows/docs.yml +55 -0
- netbox_super_cli-1.0.0/.github/workflows/e2e.yml +42 -0
- netbox_super_cli-1.0.0/.github/workflows/lint.yml +56 -0
- netbox_super_cli-1.0.0/.github/workflows/release.yml +163 -0
- netbox_super_cli-1.0.0/.github/workflows/test.yml +27 -0
- netbox_super_cli-1.0.0/.gitignore +37 -0
- netbox_super_cli-1.0.0/.pre-commit-config.yaml +35 -0
- netbox_super_cli-1.0.0/.python-version +1 -0
- netbox_super_cli-1.0.0/AGENTS.md +45 -0
- netbox_super_cli-1.0.0/CHANGELOG.md +268 -0
- netbox_super_cli-1.0.0/CLAUDE.md +38 -0
- netbox_super_cli-1.0.0/LICENSE +201 -0
- netbox_super_cli-1.0.0/PKG-INFO +182 -0
- netbox_super_cli-1.0.0/README.md +155 -0
- netbox_super_cli-1.0.0/docs/architecture/caching.md +46 -0
- netbox_super_cli-1.0.0/docs/architecture/command-generation.md +56 -0
- netbox_super_cli-1.0.0/docs/architecture/http-client.md +44 -0
- netbox_super_cli-1.0.0/docs/architecture/overview.md +44 -0
- netbox_super_cli-1.0.0/docs/architecture/schema-loading.md +46 -0
- netbox_super_cli-1.0.0/docs/contributing/adding-bundled-schemas.md +61 -0
- netbox_super_cli-1.0.0/docs/contributing/development.md +49 -0
- netbox_super_cli-1.0.0/docs/contributing/release-process.md +42 -0
- netbox_super_cli-1.0.0/docs/getting-started/concepts.md +92 -0
- netbox_super_cli-1.0.0/docs/getting-started/first-run.md +72 -0
- netbox_super_cli-1.0.0/docs/getting-started/install.md +62 -0
- netbox_super_cli-1.0.0/docs/guides/ci-and-automation.md +98 -0
- netbox_super_cli-1.0.0/docs/guides/managing-profiles.md +73 -0
- netbox_super_cli-1.0.0/docs/guides/output-formats.md +61 -0
- netbox_super_cli-1.0.0/docs/guides/using-with-ai-agents.md +71 -0
- netbox_super_cli-1.0.0/docs/guides/working-with-plugins.md +46 -0
- netbox_super_cli-1.0.0/docs/guides/writes-and-safety.md +95 -0
- netbox_super_cli-1.0.0/docs/index.md +36 -0
- netbox_super_cli-1.0.0/docs/reference/cli.md +1648 -0
- netbox_super_cli-1.0.0/docs/reference/config.md +35 -0
- netbox_super_cli-1.0.0/docs/reference/exit-codes.md +24 -0
- netbox_super_cli-1.0.0/docs/reference/schemas.md +10 -0
- netbox_super_cli-1.0.0/justfile +62 -0
- netbox_super_cli-1.0.0/mkdocs.yml +79 -0
- netbox_super_cli-1.0.0/nsc/__init__.py +5 -0
- netbox_super_cli-1.0.0/nsc/__main__.py +6 -0
- netbox_super_cli-1.0.0/nsc/_version.py +3 -0
- netbox_super_cli-1.0.0/nsc/aliases/__init__.py +24 -0
- netbox_super_cli-1.0.0/nsc/aliases/resolver.py +112 -0
- netbox_super_cli-1.0.0/nsc/auth/__init__.py +5 -0
- netbox_super_cli-1.0.0/nsc/auth/verify.py +143 -0
- netbox_super_cli-1.0.0/nsc/builder/__init__.py +5 -0
- netbox_super_cli-1.0.0/nsc/builder/build.py +514 -0
- netbox_super_cli-1.0.0/nsc/cache/__init__.py +5 -0
- netbox_super_cli-1.0.0/nsc/cache/store.py +295 -0
- netbox_super_cli-1.0.0/nsc/cli/__init__.py +1 -0
- netbox_super_cli-1.0.0/nsc/cli/aliases_commands.py +264 -0
- netbox_super_cli-1.0.0/nsc/cli/app.py +291 -0
- netbox_super_cli-1.0.0/nsc/cli/cache_commands.py +159 -0
- netbox_super_cli-1.0.0/nsc/cli/commands_dump.py +57 -0
- netbox_super_cli-1.0.0/nsc/cli/config_commands.py +156 -0
- netbox_super_cli-1.0.0/nsc/cli/globals.py +65 -0
- netbox_super_cli-1.0.0/nsc/cli/handlers.py +660 -0
- netbox_super_cli-1.0.0/nsc/cli/init_commands.py +95 -0
- netbox_super_cli-1.0.0/nsc/cli/login_commands.py +265 -0
- netbox_super_cli-1.0.0/nsc/cli/profiles_commands.py +256 -0
- netbox_super_cli-1.0.0/nsc/cli/registration.py +465 -0
- netbox_super_cli-1.0.0/nsc/cli/runtime.py +290 -0
- netbox_super_cli-1.0.0/nsc/cli/skill_commands.py +186 -0
- netbox_super_cli-1.0.0/nsc/cli/writes/__init__.py +10 -0
- netbox_super_cli-1.0.0/nsc/cli/writes/apply.py +177 -0
- netbox_super_cli-1.0.0/nsc/cli/writes/bulk.py +231 -0
- netbox_super_cli-1.0.0/nsc/cli/writes/coercion.py +9 -0
- netbox_super_cli-1.0.0/nsc/cli/writes/confirmation.py +96 -0
- netbox_super_cli-1.0.0/nsc/cli/writes/input.py +358 -0
- netbox_super_cli-1.0.0/nsc/cli/writes/preflight.py +182 -0
- netbox_super_cli-1.0.0/nsc/config/__init__.py +23 -0
- netbox_super_cli-1.0.0/nsc/config/loader.py +69 -0
- netbox_super_cli-1.0.0/nsc/config/models.py +54 -0
- netbox_super_cli-1.0.0/nsc/config/settings.py +36 -0
- netbox_super_cli-1.0.0/nsc/config/writer.py +207 -0
- netbox_super_cli-1.0.0/nsc/http/__init__.py +6 -0
- netbox_super_cli-1.0.0/nsc/http/audit.py +183 -0
- netbox_super_cli-1.0.0/nsc/http/client.py +365 -0
- netbox_super_cli-1.0.0/nsc/http/errors.py +35 -0
- netbox_super_cli-1.0.0/nsc/http/retry.py +90 -0
- netbox_super_cli-1.0.0/nsc/model/__init__.py +23 -0
- netbox_super_cli-1.0.0/nsc/model/command_model.py +125 -0
- netbox_super_cli-1.0.0/nsc/output/__init__.py +1 -0
- netbox_super_cli-1.0.0/nsc/output/csv_.py +34 -0
- netbox_super_cli-1.0.0/nsc/output/errors.py +346 -0
- netbox_super_cli-1.0.0/nsc/output/explain.py +194 -0
- netbox_super_cli-1.0.0/nsc/output/flatten.py +25 -0
- netbox_super_cli-1.0.0/nsc/output/headers.py +9 -0
- netbox_super_cli-1.0.0/nsc/output/json_.py +21 -0
- netbox_super_cli-1.0.0/nsc/output/jsonl.py +21 -0
- netbox_super_cli-1.0.0/nsc/output/render.py +47 -0
- netbox_super_cli-1.0.0/nsc/output/table.py +50 -0
- netbox_super_cli-1.0.0/nsc/output/yaml_.py +28 -0
- netbox_super_cli-1.0.0/nsc/schema/__init__.py +1 -0
- netbox_super_cli-1.0.0/nsc/schema/hashing.py +24 -0
- netbox_super_cli-1.0.0/nsc/schema/loader.py +66 -0
- netbox_super_cli-1.0.0/nsc/schema/models.py +109 -0
- netbox_super_cli-1.0.0/nsc/schema/source.py +120 -0
- netbox_super_cli-1.0.0/nsc/schemas/__init__.py +1 -0
- netbox_super_cli-1.0.0/nsc/schemas/bundled/__init__.py +1 -0
- netbox_super_cli-1.0.0/nsc/schemas/bundled/manifest.yaml +5 -0
- netbox_super_cli-1.0.0/nsc/schemas/bundled/netbox-4.6.0-beta2.json.gz +0 -0
- netbox_super_cli-1.0.0/nsc/skill/__init__.py +35 -0
- netbox_super_cli-1.0.0/pyproject.toml +105 -0
- netbox_super_cli-1.0.0/scripts/gen_docs.py +267 -0
- netbox_super_cli-1.0.0/scripts/sync_agents_md.py +71 -0
- netbox_super_cli-1.0.0/skills/netbox-super-cli/SKILL.md +127 -0
- netbox_super_cli-1.0.0/tests/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/aliases/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/aliases/test_resolver.py +198 -0
- netbox_super_cli-1.0.0/tests/auth/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/auth/test_verify.py +84 -0
- netbox_super_cli-1.0.0/tests/benchmarks/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/benchmarks/test_startup.py +46 -0
- netbox_super_cli-1.0.0/tests/builder/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/builder/test_build.py +149 -0
- netbox_super_cli-1.0.0/tests/builder/test_default_columns.py +233 -0
- netbox_super_cli-1.0.0/tests/builder/test_redaction_marking.py +205 -0
- netbox_super_cli-1.0.0/tests/builder/test_request_body_shape.py +227 -0
- netbox_super_cli-1.0.0/tests/cache/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/cache/test_prune.py +253 -0
- netbox_super_cli-1.0.0/tests/cache/test_store.py +120 -0
- netbox_super_cli-1.0.0/tests/cli/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/cli/test_aliases_commands.py +322 -0
- netbox_super_cli-1.0.0/tests/cli/test_app_smoke.py +89 -0
- netbox_super_cli-1.0.0/tests/cli/test_cache_commands.py +129 -0
- netbox_super_cli-1.0.0/tests/cli/test_commands_dump.py +44 -0
- netbox_super_cli-1.0.0/tests/cli/test_completion_smoke.py +26 -0
- netbox_super_cli-1.0.0/tests/cli/test_config_commands.py +140 -0
- netbox_super_cli-1.0.0/tests/cli/test_explain.py +175 -0
- netbox_super_cli-1.0.0/tests/cli/test_handlers.py +198 -0
- netbox_super_cli-1.0.0/tests/cli/test_init_commands.py +83 -0
- netbox_super_cli-1.0.0/tests/cli/test_login_commands.py +159 -0
- netbox_super_cli-1.0.0/tests/cli/test_ndjson_input.py +154 -0
- netbox_super_cli-1.0.0/tests/cli/test_profiles_commands.py +174 -0
- netbox_super_cli-1.0.0/tests/cli/test_registration.py +338 -0
- netbox_super_cli-1.0.0/tests/cli/test_respx_integration.py +168 -0
- netbox_super_cli-1.0.0/tests/cli/test_runtime.py +153 -0
- netbox_super_cli-1.0.0/tests/cli/test_skill_commands.py +124 -0
- netbox_super_cli-1.0.0/tests/cli/test_stdin_sniffer.py +67 -0
- netbox_super_cli-1.0.0/tests/cli/test_writes_respx.py +597 -0
- netbox_super_cli-1.0.0/tests/cli/writes/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/cli/writes/test_apply.py +286 -0
- netbox_super_cli-1.0.0/tests/cli/writes/test_bulk.py +302 -0
- netbox_super_cli-1.0.0/tests/cli/writes/test_confirmation.py +87 -0
- netbox_super_cli-1.0.0/tests/cli/writes/test_handler_helpers.py +130 -0
- netbox_super_cli-1.0.0/tests/cli/writes/test_handlers_audit.py +51 -0
- netbox_super_cli-1.0.0/tests/cli/writes/test_input.py +179 -0
- netbox_super_cli-1.0.0/tests/cli/writes/test_preflight.py +226 -0
- netbox_super_cli-1.0.0/tests/config/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/config/test_loader.py +152 -0
- netbox_super_cli-1.0.0/tests/config/test_models.py +45 -0
- netbox_super_cli-1.0.0/tests/config/test_writer_atomicity.py +59 -0
- netbox_super_cli-1.0.0/tests/config/test_writer_dotted_paths.py +78 -0
- netbox_super_cli-1.0.0/tests/config/test_writer_roundtrip.py +31 -0
- netbox_super_cli-1.0.0/tests/conftest.py +41 -0
- netbox_super_cli-1.0.0/tests/e2e/README.md +128 -0
- netbox_super_cli-1.0.0/tests/e2e/__init__.py +1 -0
- netbox_super_cli-1.0.0/tests/e2e/conftest.py +176 -0
- netbox_super_cli-1.0.0/tests/e2e/docker-compose.yml +55 -0
- netbox_super_cli-1.0.0/tests/e2e/test_audit_redaction.py +83 -0
- netbox_super_cli-1.0.0/tests/e2e/test_auth_error_envelope.py +23 -0
- netbox_super_cli-1.0.0/tests/e2e/test_bulk_create_with_loop_fallback.py +87 -0
- netbox_super_cli-1.0.0/tests/e2e/test_full_cycle.py +164 -0
- netbox_super_cli-1.0.0/tests/e2e/test_login.py +134 -0
- netbox_super_cli-1.0.0/tests/e2e/test_ndjson.py +88 -0
- netbox_super_cli-1.0.0/tests/e2e/test_preflight_blocks_apply.py +35 -0
- netbox_super_cli-1.0.0/tests/e2e/test_validation_error_envelope_from_netbox.py +41 -0
- netbox_super_cli-1.0.0/tests/e2e/wait_for_netbox.sh +74 -0
- netbox_super_cli-1.0.0/tests/fixtures/profiles/single_profile.yaml +8 -0
- netbox_super_cli-1.0.0/tests/fixtures/responses/auth_401.json +1 -0
- netbox_super_cli-1.0.0/tests/fixtures/responses/circuits_providers_list.json +9 -0
- netbox_super_cli-1.0.0/tests/fixtures/responses/dcim_devices_get.json +1 -0
- netbox_super_cli-1.0.0/tests/fixtures/responses/dcim_devices_list_p1.json +9 -0
- netbox_super_cli-1.0.0/tests/fixtures/responses/dcim_devices_list_p2.json +8 -0
- netbox_super_cli-1.0.0/tests/http/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/http/test_audit.py +148 -0
- netbox_super_cli-1.0.0/tests/http/test_audit_redaction.py +110 -0
- netbox_super_cli-1.0.0/tests/http/test_client.py +384 -0
- netbox_super_cli-1.0.0/tests/http/test_client_redaction_threading.py +43 -0
- netbox_super_cli-1.0.0/tests/http/test_errors.py +31 -0
- netbox_super_cli-1.0.0/tests/http/test_retry.py +136 -0
- netbox_super_cli-1.0.0/tests/model/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/model/test_command_model.py +95 -0
- netbox_super_cli-1.0.0/tests/model/test_request_body_shape.py +89 -0
- netbox_super_cli-1.0.0/tests/output/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/output/test_csv.py +52 -0
- netbox_super_cli-1.0.0/tests/output/test_errors.py +252 -0
- netbox_super_cli-1.0.0/tests/output/test_errors_aliases.py +103 -0
- netbox_super_cli-1.0.0/tests/output/test_explain.py +80 -0
- netbox_super_cli-1.0.0/tests/output/test_flatten.py +34 -0
- netbox_super_cli-1.0.0/tests/output/test_input_error_envelope.py +46 -0
- netbox_super_cli-1.0.0/tests/output/test_json.py +25 -0
- netbox_super_cli-1.0.0/tests/output/test_jsonl.py +21 -0
- netbox_super_cli-1.0.0/tests/output/test_render_dispatch.py +69 -0
- netbox_super_cli-1.0.0/tests/output/test_table.py +59 -0
- netbox_super_cli-1.0.0/tests/output/test_yaml.py +33 -0
- netbox_super_cli-1.0.0/tests/schema/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/schema/test_hashing.py +31 -0
- netbox_super_cli-1.0.0/tests/schema/test_loader.py +81 -0
- netbox_super_cli-1.0.0/tests/schema/test_models.py +66 -0
- netbox_super_cli-1.0.0/tests/schema/test_source.py +159 -0
- netbox_super_cli-1.0.0/tests/scripts/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/scripts/test_gen_docs.py +95 -0
- netbox_super_cli-1.0.0/tests/skill/__init__.py +0 -0
- netbox_super_cli-1.0.0/tests/skill/test_bundle.py +66 -0
- netbox_super_cli-1.0.0/uv.lock +1191 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: agents-md-sync
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
paths:
|
|
6
|
+
- "CLAUDE.md"
|
|
7
|
+
- "AGENTS.md"
|
|
8
|
+
- "scripts/sync_agents_md.py"
|
|
9
|
+
- ".github/workflows/agents-md-sync.yml"
|
|
10
|
+
push:
|
|
11
|
+
branches:
|
|
12
|
+
- main
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
check:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@v3
|
|
25
|
+
with:
|
|
26
|
+
enable-cache: true
|
|
27
|
+
|
|
28
|
+
- name: Install Python
|
|
29
|
+
run: uv python install 3.12
|
|
30
|
+
|
|
31
|
+
- name: Sync deps
|
|
32
|
+
run: uv sync --frozen
|
|
33
|
+
|
|
34
|
+
- name: Check AGENTS.md is in sync with CLAUDE.md
|
|
35
|
+
run: uv run python scripts/sync_agents_md.py --check
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: bench
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
startup-time:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v4
|
|
15
|
+
- name: Install
|
|
16
|
+
run: uv sync --all-extras --dev
|
|
17
|
+
- name: Benchmark
|
|
18
|
+
run: NSC_BENCH=1 uv run pytest tests/benchmarks/ -v -s
|
|
19
|
+
continue-on-error: true
|
|
20
|
+
- name: Step summary
|
|
21
|
+
if: always()
|
|
22
|
+
run: |
|
|
23
|
+
echo "Bench job ran. See the previous step's stdout for the median timing." >> "$GITHUB_STEP_SUMMARY"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
paths:
|
|
6
|
+
- "docs/**"
|
|
7
|
+
- "mkdocs.yml"
|
|
8
|
+
- "scripts/gen_docs.py"
|
|
9
|
+
- ".github/workflows/docs.yml"
|
|
10
|
+
push:
|
|
11
|
+
tags:
|
|
12
|
+
- "v*"
|
|
13
|
+
|
|
14
|
+
# Permissions for the deploy job (idempotent on the build-only PR job).
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
pages: write
|
|
18
|
+
id-token: write
|
|
19
|
+
|
|
20
|
+
# Only one in-flight deploy at a time; cancel queued ones.
|
|
21
|
+
concurrency:
|
|
22
|
+
group: pages
|
|
23
|
+
cancel-in-progress: false
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
build:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
- name: Install uv
|
|
31
|
+
uses: astral-sh/setup-uv@v3
|
|
32
|
+
with:
|
|
33
|
+
enable-cache: true
|
|
34
|
+
- name: Install Python
|
|
35
|
+
run: uv python install 3.12
|
|
36
|
+
- name: Sync deps (runtime + docs)
|
|
37
|
+
run: uv sync --frozen --group docs
|
|
38
|
+
- name: Build site (strict)
|
|
39
|
+
run: uv run mkdocs build --strict
|
|
40
|
+
- name: Upload Pages artifact
|
|
41
|
+
if: github.ref_type == 'tag'
|
|
42
|
+
uses: actions/upload-pages-artifact@v3
|
|
43
|
+
with:
|
|
44
|
+
path: site
|
|
45
|
+
|
|
46
|
+
deploy:
|
|
47
|
+
needs: build
|
|
48
|
+
if: github.ref_type == 'tag'
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
environment:
|
|
51
|
+
name: github-pages
|
|
52
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
53
|
+
steps:
|
|
54
|
+
- id: deployment
|
|
55
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: e2e
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
paths:
|
|
8
|
+
- 'nsc/http/**'
|
|
9
|
+
- 'nsc/cli/handlers.py'
|
|
10
|
+
- 'nsc/cli/registration.py'
|
|
11
|
+
- 'nsc/cli/writes/**'
|
|
12
|
+
- 'nsc/output/explain.py'
|
|
13
|
+
- 'nsc/output/errors.py'
|
|
14
|
+
- 'tests/e2e/**'
|
|
15
|
+
- '.github/workflows/e2e.yml'
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
e2e:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
timeout-minutes: 15
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: astral-sh/setup-uv@v3
|
|
25
|
+
- name: Sync deps
|
|
26
|
+
run: uv sync --frozen
|
|
27
|
+
- name: Start NetBox
|
|
28
|
+
run: docker compose -f tests/e2e/docker-compose.yml up -d
|
|
29
|
+
- name: Wait for NetBox
|
|
30
|
+
run: tests/e2e/wait_for_netbox.sh
|
|
31
|
+
- name: Run e2e tests
|
|
32
|
+
env:
|
|
33
|
+
NSC_E2E: '1'
|
|
34
|
+
NSC_URL: http://127.0.0.1:8080
|
|
35
|
+
NSC_TOKEN: '0123456789abcdef0123456789abcdef01234567'
|
|
36
|
+
run: uv run pytest tests/e2e/ -v
|
|
37
|
+
- name: Dump container logs on failure
|
|
38
|
+
if: failure()
|
|
39
|
+
run: docker compose -f tests/e2e/docker-compose.yml logs
|
|
40
|
+
- name: Stop NetBox
|
|
41
|
+
if: always()
|
|
42
|
+
run: docker compose -f tests/e2e/docker-compose.yml down -v
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: lint
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- name: Install uv
|
|
14
|
+
uses: astral-sh/setup-uv@v3
|
|
15
|
+
with:
|
|
16
|
+
enable-cache: true
|
|
17
|
+
- name: Install Python
|
|
18
|
+
run: uv python install 3.12
|
|
19
|
+
- name: Sync deps
|
|
20
|
+
run: uv sync --frozen
|
|
21
|
+
- name: Ruff check
|
|
22
|
+
run: uv run ruff check nsc tests scripts
|
|
23
|
+
- name: Ruff format
|
|
24
|
+
run: uv run ruff format --check nsc tests scripts
|
|
25
|
+
- name: Mypy strict
|
|
26
|
+
run: uv run mypy --strict nsc scripts
|
|
27
|
+
|
|
28
|
+
docs-reference-fresh:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
- name: Install uv
|
|
33
|
+
uses: astral-sh/setup-uv@v3
|
|
34
|
+
with:
|
|
35
|
+
enable-cache: true
|
|
36
|
+
- name: Install Python
|
|
37
|
+
run: uv python install 3.12
|
|
38
|
+
- name: Sync deps (runtime + docs — gen_docs needs both)
|
|
39
|
+
run: uv sync --frozen --group docs
|
|
40
|
+
- name: Verify auto-generated reference pages are up to date
|
|
41
|
+
run: uv run python scripts/gen_docs.py --check
|
|
42
|
+
|
|
43
|
+
agents-md-fresh:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- name: Install uv
|
|
48
|
+
uses: astral-sh/setup-uv@v3
|
|
49
|
+
with:
|
|
50
|
+
enable-cache: true
|
|
51
|
+
- name: Install Python
|
|
52
|
+
run: uv python install 3.12
|
|
53
|
+
- name: Sync deps
|
|
54
|
+
run: uv sync --frozen
|
|
55
|
+
- name: Check AGENTS.md is in sync with CLAUDE.md
|
|
56
|
+
run: uv run python scripts/sync_agents_md.py --check
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Tag-only trigger. NEVER on pull_request or branch push — the trusted
|
|
4
|
+
# publisher is bound to this workflow filename + the v* tag pattern, so a
|
|
5
|
+
# stray dispatch on main would either fail authentication (good) or, if
|
|
6
|
+
# someone misconfigured PyPI, publish a bogus release (very bad).
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags:
|
|
10
|
+
# Release versions: v<digit><anything>.<digit><anything>.<digit><anything>.
|
|
11
|
+
# GitHub's tag filter uses fnmatch (not regex), so the leading [0-9]
|
|
12
|
+
# excludes vfoobar / vNEXT entirely, but the unbounded `*` after each
|
|
13
|
+
# digit class also matches v1.0.0-rc1 and v1.0.0.1. Those slip through
|
|
14
|
+
# the trigger and are caught by the version-match step below — the
|
|
15
|
+
# filter narrows the obvious garbage; the version-match step is the
|
|
16
|
+
# actual gate.
|
|
17
|
+
- "v[0-9]*.[0-9]*.[0-9]*"
|
|
18
|
+
|
|
19
|
+
# Trusted publishing requires id-token: write at the workflow level so the
|
|
20
|
+
# OIDC token issued to pypa/gh-action-pypi-publish is signed.
|
|
21
|
+
permissions:
|
|
22
|
+
contents: write # gh release create
|
|
23
|
+
id-token: write # PyPI trusted publishing OIDC
|
|
24
|
+
|
|
25
|
+
# One in-flight release at a time; queue rather than cancel so a fast
|
|
26
|
+
# re-tag never overlaps the previous publish.
|
|
27
|
+
concurrency:
|
|
28
|
+
group: release
|
|
29
|
+
cancel-in-progress: false
|
|
30
|
+
|
|
31
|
+
jobs:
|
|
32
|
+
release:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
# PyPI trusted publishing reads the workflow filename + repo + tag-ref
|
|
35
|
+
# from the OIDC claim. No environment is required for PyPI; we
|
|
36
|
+
# explicitly do NOT set an `environment:` block here — adding one would
|
|
37
|
+
# change the OIDC subject claim and break the trusted-publisher binding
|
|
38
|
+
# the user registered against the no-environment form (per §Pre-tag
|
|
39
|
+
# prerequisites).
|
|
40
|
+
steps:
|
|
41
|
+
- name: Checkout (full history for changelog notes)
|
|
42
|
+
uses: actions/checkout@v4
|
|
43
|
+
with:
|
|
44
|
+
fetch-depth: 0
|
|
45
|
+
|
|
46
|
+
- name: Verify tag is on main
|
|
47
|
+
run: |
|
|
48
|
+
set -euo pipefail
|
|
49
|
+
# Trusted publishing's OIDC claim is bound to the workflow filename
|
|
50
|
+
# and tag ref — it does NOT enforce that the tagged commit is on
|
|
51
|
+
# main. Tags pushed from a topic branch (or any unmerged commit)
|
|
52
|
+
# would otherwise publish from un-reviewed code. This guard rejects
|
|
53
|
+
# any tag whose commit is not reachable from origin/main.
|
|
54
|
+
# actions/checkout@v4 with fetch-depth: 0 fetches the tag's full
|
|
55
|
+
# history but does not populate origin/main as a ref locally; we
|
|
56
|
+
# fetch it explicitly here.
|
|
57
|
+
git fetch origin main
|
|
58
|
+
if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
|
|
59
|
+
echo "Tag $GITHUB_REF_NAME points at $GITHUB_SHA which is not reachable from main." >&2
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
echo "Tag $GITHUB_REF_NAME ($GITHUB_SHA) is on main."
|
|
63
|
+
|
|
64
|
+
- name: Install uv
|
|
65
|
+
uses: astral-sh/setup-uv@v3
|
|
66
|
+
with:
|
|
67
|
+
enable-cache: true
|
|
68
|
+
|
|
69
|
+
- name: Install Python
|
|
70
|
+
run: uv python install 3.12
|
|
71
|
+
|
|
72
|
+
- name: Sync runtime deps (so the next step can import nsc._version)
|
|
73
|
+
run: uv sync --frozen
|
|
74
|
+
|
|
75
|
+
- name: Verify tag matches package version
|
|
76
|
+
run: |
|
|
77
|
+
set -euo pipefail
|
|
78
|
+
tag_version="${GITHUB_REF_NAME#v}"
|
|
79
|
+
pkg_version="$(uv run python -c 'from nsc._version import __version__; print(__version__)')"
|
|
80
|
+
if [ "$tag_version" != "$pkg_version" ]; then
|
|
81
|
+
echo "Tag $GITHUB_REF_NAME -> version $tag_version does not match nsc/_version.py $pkg_version" >&2
|
|
82
|
+
exit 1
|
|
83
|
+
fi
|
|
84
|
+
echo "Tag $GITHUB_REF_NAME matches package version $pkg_version"
|
|
85
|
+
|
|
86
|
+
- name: Build sdist + wheel
|
|
87
|
+
run: |
|
|
88
|
+
rm -rf dist
|
|
89
|
+
uv build --out-dir dist
|
|
90
|
+
ls -la dist/
|
|
91
|
+
|
|
92
|
+
- name: Verify wheel filename matches tag
|
|
93
|
+
run: |
|
|
94
|
+
set -euo pipefail
|
|
95
|
+
tag_version="${GITHUB_REF_NAME#v}"
|
|
96
|
+
if ! ls "dist/netbox_super_cli-${tag_version}-py3-none-any.whl" >/dev/null 2>&1; then
|
|
97
|
+
echo "Wheel for ${tag_version} not found in dist/" >&2
|
|
98
|
+
ls dist/ >&2
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
if ! ls "dist/netbox_super_cli-${tag_version}.tar.gz" >/dev/null 2>&1; then
|
|
102
|
+
echo "Sdist for ${tag_version} not found in dist/" >&2
|
|
103
|
+
ls dist/ >&2
|
|
104
|
+
exit 1
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
- name: Publish to PyPI (trusted publishing)
|
|
108
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
109
|
+
with:
|
|
110
|
+
# No `password:` — trusted publishing uses the OIDC token bound
|
|
111
|
+
# to id-token: write above. PyPI must have a Trusted Publisher
|
|
112
|
+
# registered for this repo + workflow + (no) environment before
|
|
113
|
+
# this step runs (see §Pre-tag prerequisites in the 5d plan).
|
|
114
|
+
packages-dir: dist
|
|
115
|
+
# Print which artifacts were uploaded; helps diagnose mismatches.
|
|
116
|
+
verbose: true
|
|
117
|
+
# Don't fail if a re-tag attempts to re-upload an existing
|
|
118
|
+
# filename: the action calls PyPI's filename-exists check and
|
|
119
|
+
# silently skips the upload (it does NOT compare byte content
|
|
120
|
+
# of the existing vs. would-be-uploaded artifact). PyPI itself
|
|
121
|
+
# treats version artifacts as immutable, so the file already on
|
|
122
|
+
# PyPI is the canonical bytes for that version. A re-tag that
|
|
123
|
+
# builds different bytes for the same version would publish
|
|
124
|
+
# those bytes only to the GitHub Release, not to PyPI — diverging
|
|
125
|
+
# the two. Avoid that by never re-tagging an already-published
|
|
126
|
+
# version; bump to the next patch version instead.
|
|
127
|
+
skip-existing: true
|
|
128
|
+
|
|
129
|
+
- name: Extract changelog section for release notes
|
|
130
|
+
id: notes
|
|
131
|
+
run: |
|
|
132
|
+
set -euo pipefail
|
|
133
|
+
tag_version="${GITHUB_REF_NAME#v}"
|
|
134
|
+
# Match `## v<ver><space>...` and capture until the next `## `
|
|
135
|
+
# heading. The trailing space after <ver> in the regex is what
|
|
136
|
+
# disambiguates `v1.0` from `v1.0.1`; what follows (typically an
|
|
137
|
+
# em-dash, date, sub-phase name) is intentionally unconstrained.
|
|
138
|
+
awk -v ver="$tag_version" '
|
|
139
|
+
$0 ~ "^## v" ver " " { capture=1; print; next }
|
|
140
|
+
capture && /^## / { exit }
|
|
141
|
+
capture { print }
|
|
142
|
+
' CHANGELOG.md > /tmp/release-notes.md
|
|
143
|
+
if [ ! -s /tmp/release-notes.md ]; then
|
|
144
|
+
echo "Release notes for v${tag_version} not found in CHANGELOG.md" >&2
|
|
145
|
+
exit 1
|
|
146
|
+
fi
|
|
147
|
+
echo "Extracted $(wc -l < /tmp/release-notes.md) lines of release notes."
|
|
148
|
+
|
|
149
|
+
- name: Create GitHub Release
|
|
150
|
+
env:
|
|
151
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
152
|
+
run: |
|
|
153
|
+
set -euo pipefail
|
|
154
|
+
tag_version="${GITHUB_REF_NAME#v}"
|
|
155
|
+
# Use exact filenames (the verify-wheel-filename step proved both
|
|
156
|
+
# exist) rather than glob-expanding `dist/*.whl dist/*.tar.gz` —
|
|
157
|
+
# that glob would silently attach any stray artifact a future
|
|
158
|
+
# build step might drop into dist/.
|
|
159
|
+
gh release create "$GITHUB_REF_NAME" \
|
|
160
|
+
--title "$GITHUB_REF_NAME" \
|
|
161
|
+
--notes-file /tmp/release-notes.md \
|
|
162
|
+
"dist/netbox_super_cli-${tag_version}-py3-none-any.whl" \
|
|
163
|
+
"dist/netbox_super_cli-${tag_version}.tar.gz"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ${{ matrix.os }}
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
os: [ubuntu-latest, macos-latest]
|
|
15
|
+
python: ["3.12", "3.13"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v3
|
|
20
|
+
with:
|
|
21
|
+
enable-cache: true
|
|
22
|
+
- name: Install Python
|
|
23
|
+
run: uv python install ${{ matrix.python }}
|
|
24
|
+
- name: Sync deps
|
|
25
|
+
run: uv sync --frozen
|
|
26
|
+
- name: Run tests
|
|
27
|
+
run: uv run pytest -v
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.pytest_cache/
|
|
6
|
+
.mypy_cache/
|
|
7
|
+
.ruff_cache/
|
|
8
|
+
build/
|
|
9
|
+
dist/
|
|
10
|
+
*.egg
|
|
11
|
+
|
|
12
|
+
# venv
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
|
|
16
|
+
# uv
|
|
17
|
+
# (uv.lock IS committed)
|
|
18
|
+
|
|
19
|
+
# IDE
|
|
20
|
+
.idea/
|
|
21
|
+
.vscode/
|
|
22
|
+
*.swp
|
|
23
|
+
.DS_Store
|
|
24
|
+
|
|
25
|
+
# nsc local state (must never be committed)
|
|
26
|
+
.nsc/
|
|
27
|
+
|
|
28
|
+
# Secrets
|
|
29
|
+
.env
|
|
30
|
+
.env.*
|
|
31
|
+
*.env
|
|
32
|
+
|
|
33
|
+
# Local-only design notes (kept out of git history)
|
|
34
|
+
docs/superpowers/
|
|
35
|
+
|
|
36
|
+
# mkdocs
|
|
37
|
+
site/
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v4.6.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: end-of-file-fixer
|
|
6
|
+
- id: trailing-whitespace
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-toml
|
|
9
|
+
- id: check-merge-conflict
|
|
10
|
+
- id: check-added-large-files
|
|
11
|
+
# 2 MB is enough now that bundled schemas are gzipped.
|
|
12
|
+
args: [--maxkb=2048]
|
|
13
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
14
|
+
rev: v0.15.12
|
|
15
|
+
hooks:
|
|
16
|
+
- id: ruff
|
|
17
|
+
args: [--fix]
|
|
18
|
+
- id: ruff-format
|
|
19
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
20
|
+
rev: v1.20.2
|
|
21
|
+
hooks:
|
|
22
|
+
- id: mypy
|
|
23
|
+
# Keep this list in sync with [project.dependencies] in pyproject.toml,
|
|
24
|
+
# plus type stubs for any third-party packages used in scripts/.
|
|
25
|
+
additional_dependencies:
|
|
26
|
+
- pydantic>=2.7
|
|
27
|
+
- typer>=0.12
|
|
28
|
+
- rich>=13.7
|
|
29
|
+
- httpx>=0.27
|
|
30
|
+
- "ruamel.yaml>=0.18"
|
|
31
|
+
- structlog>=24.1
|
|
32
|
+
- platformdirs>=4.2
|
|
33
|
+
- types-PyYAML>=6.0
|
|
34
|
+
args: [--strict]
|
|
35
|
+
files: ^(nsc|scripts)/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AGENTS.md is auto-generated from CLAUDE.md by scripts/sync_agents_md.py.
|
|
3
|
+
Do NOT edit this file directly — edit CLAUDE.md and run
|
|
4
|
+
`uv run python scripts/sync_agents_md.py` to regenerate. CI fails the PR
|
|
5
|
+
if this file drifts from CLAUDE.md (see .github/workflows/lint.yml).
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# Working on netbox-super-cli (for AI agents)
|
|
9
|
+
|
|
10
|
+
This is the contributor guide for AI agents (and humans!) modifying this repo. The end-user-facing AI guide is the bundled Skill at `skills/netbox-super-cli/SKILL.md` (added in Phase 5).
|
|
11
|
+
|
|
12
|
+
## Architecture cheat sheet
|
|
13
|
+
|
|
14
|
+
- `nsc/schema/` — parses an OpenAPI document into Pydantic models. Knows nothing about CLIs.
|
|
15
|
+
- `nsc/model/` — the normalized command tree (data only, framework-free). The "brain".
|
|
16
|
+
- `nsc/builder/` — converts a parsed schema into a `CommandModel`.
|
|
17
|
+
- `nsc/cli/` — the Typer app; consumes a `CommandModel`.
|
|
18
|
+
- `nsc/http/` — thin httpx wrapper (Phase 2+).
|
|
19
|
+
- `nsc/output/` — formatters (Phase 2+).
|
|
20
|
+
- `nsc/config/` — config loader + Pydantic models (Phase 1 has a minimal version).
|
|
21
|
+
- `nsc/cache/` — disk cache for generated command-models.
|
|
22
|
+
- `nsc/schemas/bundled/` — versioned NetBox OpenAPI snapshots, fallback when offline.
|
|
23
|
+
|
|
24
|
+
The hard rule: **`nsc/model/` imports nothing from `nsc/cli/`, `nsc/http/`, or any framework.** If you need to add a dependency to `model/`, you're probably solving the wrong problem.
|
|
25
|
+
|
|
26
|
+
## Common commands
|
|
27
|
+
|
|
28
|
+
- `just sync` — install/refresh deps.
|
|
29
|
+
- `just test` — run all tests.
|
|
30
|
+
- `just lint` — ruff + mypy --strict.
|
|
31
|
+
- `just fix` — auto-fix ruff issues.
|
|
32
|
+
- `just nsc <args>` — run the local CLI.
|
|
33
|
+
|
|
34
|
+
## Conventions
|
|
35
|
+
|
|
36
|
+
- Python 3.12+, full type annotations, `mypy --strict`.
|
|
37
|
+
- Pydantic v2 for all structured data.
|
|
38
|
+
- Conventional Commits.
|
|
39
|
+
- TDD: write the failing test first.
|
|
40
|
+
- No comments on what code does; only on non-obvious *why*.
|
|
41
|
+
|
|
42
|
+
## Where the design lives
|
|
43
|
+
|
|
44
|
+
- `docs/superpowers/specs/2026-04-30-netbox-super-cli-design.md` — the full design.
|
|
45
|
+
- `docs/superpowers/plans/` — implementation plans, one per phase.
|