panorama-super-cli 0.1.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.
- panorama_super_cli-0.1.0/.github/CODEOWNERS +2 -0
- panorama_super_cli-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +42 -0
- panorama_super_cli-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +28 -0
- panorama_super_cli-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +20 -0
- panorama_super_cli-0.1.0/.github/dependabot.yml +13 -0
- panorama_super_cli-0.1.0/.github/workflows/docs.yml +52 -0
- panorama_super_cli-0.1.0/.github/workflows/lint.yml +41 -0
- panorama_super_cli-0.1.0/.github/workflows/release.yml +112 -0
- panorama_super_cli-0.1.0/.github/workflows/test.yml +27 -0
- panorama_super_cli-0.1.0/.gitignore +38 -0
- panorama_super_cli-0.1.0/.pre-commit-config.yaml +31 -0
- panorama_super_cli-0.1.0/.python-version +1 -0
- panorama_super_cli-0.1.0/AGENTS.md +97 -0
- panorama_super_cli-0.1.0/CHANGELOG.md +51 -0
- panorama_super_cli-0.1.0/CLAUDE.md +90 -0
- panorama_super_cli-0.1.0/LICENSE +202 -0
- panorama_super_cli-0.1.0/PKG-INFO +110 -0
- panorama_super_cli-0.1.0/README.md +79 -0
- panorama_super_cli-0.1.0/docs/contributing/branching.md +39 -0
- panorama_super_cli-0.1.0/docs/contributing/development.md +60 -0
- panorama_super_cli-0.1.0/docs/contributing/release-process.md +40 -0
- panorama_super_cli-0.1.0/docs/getting-started/concepts.md +48 -0
- panorama_super_cli-0.1.0/docs/getting-started/first-run.md +66 -0
- panorama_super_cli-0.1.0/docs/getting-started/install.md +50 -0
- panorama_super_cli-0.1.0/docs/guides/duplicates-and-merging.md +87 -0
- panorama_super_cli-0.1.0/docs/guides/finding-objects.md +67 -0
- panorama_super_cli-0.1.0/docs/guides/live-vs-offline.md +56 -0
- panorama_super_cli-0.1.0/docs/guides/naming.md +58 -0
- panorama_super_cli-0.1.0/docs/guides/output-formats.md +62 -0
- panorama_super_cli-0.1.0/docs/guides/references-and-audit.md +65 -0
- panorama_super_cli-0.1.0/docs/guides/safety.md +61 -0
- panorama_super_cli-0.1.0/docs/guides/using-with-ai-agents.md +59 -0
- panorama_super_cli-0.1.0/docs/index.md +46 -0
- panorama_super_cli-0.1.0/docs/reference/cli.md +78 -0
- panorama_super_cli-0.1.0/docs/reference/config.md +61 -0
- panorama_super_cli-0.1.0/docs/reference/exit-codes.md +45 -0
- panorama_super_cli-0.1.0/justfile +49 -0
- panorama_super_cli-0.1.0/mkdocs.yml +72 -0
- panorama_super_cli-0.1.0/psc/__init__.py +5 -0
- panorama_super_cli-0.1.0/psc/__main__.py +6 -0
- panorama_super_cli-0.1.0/psc/_version.py +7 -0
- panorama_super_cli-0.1.0/psc/cli/__init__.py +1 -0
- panorama_super_cli-0.1.0/psc/cli/_plan.py +62 -0
- panorama_super_cli-0.1.0/psc/cli/app.py +136 -0
- panorama_super_cli-0.1.0/psc/cli/dedup_cmds.py +94 -0
- panorama_super_cli-0.1.0/psc/cli/find_cmds.py +92 -0
- panorama_super_cli-0.1.0/psc/cli/name_cmds.py +101 -0
- panorama_super_cli-0.1.0/psc/cli/profile_cmds.py +68 -0
- panorama_super_cli-0.1.0/psc/cli/refs_cmds.py +95 -0
- panorama_super_cli-0.1.0/psc/cli/runtime.py +87 -0
- panorama_super_cli-0.1.0/psc/config/__init__.py +6 -0
- panorama_super_cli-0.1.0/psc/config/loader.py +49 -0
- panorama_super_cli-0.1.0/psc/config/models.py +41 -0
- panorama_super_cli-0.1.0/psc/core/__init__.py +6 -0
- panorama_super_cli-0.1.0/psc/core/apply_xml.py +176 -0
- panorama_super_cli-0.1.0/psc/core/changeset.py +127 -0
- panorama_super_cli-0.1.0/psc/core/dedup.py +203 -0
- panorama_super_cli-0.1.0/psc/core/models.py +235 -0
- panorama_super_cli-0.1.0/psc/core/naming.py +176 -0
- panorama_super_cli-0.1.0/psc/core/normalize.py +184 -0
- panorama_super_cli-0.1.0/psc/core/parse.py +270 -0
- panorama_super_cli-0.1.0/psc/core/refs.py +370 -0
- panorama_super_cli-0.1.0/psc/core/resolve.py +186 -0
- panorama_super_cli-0.1.0/psc/core/setcmd.py +180 -0
- panorama_super_cli-0.1.0/psc/core/source.py +119 -0
- panorama_super_cli-0.1.0/psc/output/__init__.py +6 -0
- panorama_super_cli-0.1.0/psc/output/errors.py +62 -0
- panorama_super_cli-0.1.0/psc/output/format.py +142 -0
- panorama_super_cli-0.1.0/pyproject.toml +115 -0
- panorama_super_cli-0.1.0/scripts/sync_agents_md.py +47 -0
- panorama_super_cli-0.1.0/skills/panorama-super-cli/SKILL.md +150 -0
- panorama_super_cli-0.1.0/tests/__init__.py +0 -0
- panorama_super_cli-0.1.0/tests/conftest.py +26 -0
- panorama_super_cli-0.1.0/tests/fixtures/panorama-config.xml +121 -0
- panorama_super_cli-0.1.0/tests/test_apply_xml.py +63 -0
- panorama_super_cli-0.1.0/tests/test_cli.py +104 -0
- panorama_super_cli-0.1.0/tests/test_dedup.py +74 -0
- panorama_super_cli-0.1.0/tests/test_hardening.py +121 -0
- panorama_super_cli-0.1.0/tests/test_naming.py +91 -0
- panorama_super_cli-0.1.0/tests/test_normalize.py +50 -0
- panorama_super_cli-0.1.0/tests/test_output.py +35 -0
- panorama_super_cli-0.1.0/tests/test_parse.py +48 -0
- panorama_super_cli-0.1.0/tests/test_refs.py +42 -0
- panorama_super_cli-0.1.0/tests/test_resolve.py +39 -0
- panorama_super_cli-0.1.0/tests/test_setcmd.py +61 -0
- panorama_super_cli-0.1.0/uv.lock +1202 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something `psc` did wrong
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened?
|
|
9
|
+
description: What you ran and what `psc` did vs. what you expected.
|
|
10
|
+
placeholder: |
|
|
11
|
+
$ psc -c export.xml dedup merge --keep a --remove b
|
|
12
|
+
...
|
|
13
|
+
validations:
|
|
14
|
+
required: true
|
|
15
|
+
- type: input
|
|
16
|
+
id: version
|
|
17
|
+
attributes:
|
|
18
|
+
label: psc version
|
|
19
|
+
description: Output of `psc --version`.
|
|
20
|
+
validations:
|
|
21
|
+
required: true
|
|
22
|
+
- type: dropdown
|
|
23
|
+
id: source
|
|
24
|
+
attributes:
|
|
25
|
+
label: Source
|
|
26
|
+
options:
|
|
27
|
+
- Offline (--config file.xml)
|
|
28
|
+
- Live (--profile)
|
|
29
|
+
validations:
|
|
30
|
+
required: true
|
|
31
|
+
- type: textarea
|
|
32
|
+
id: env
|
|
33
|
+
attributes:
|
|
34
|
+
label: Environment
|
|
35
|
+
description: OS, Python version, install method (uv/pipx/pip).
|
|
36
|
+
- type: checkboxes
|
|
37
|
+
id: sanitized
|
|
38
|
+
attributes:
|
|
39
|
+
label: Confirmation
|
|
40
|
+
options:
|
|
41
|
+
- label: I have removed any real IPs, hostnames, API keys, or other sensitive config from this report.
|
|
42
|
+
required: true
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: A new subtool, flag, or capability
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: What object-management problem are you trying to solve?
|
|
9
|
+
placeholder: "I want to find every object referenced only by disabled rules so I can clean them up."
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: proposal
|
|
14
|
+
attributes:
|
|
15
|
+
label: Proposed command / behaviour
|
|
16
|
+
placeholder: "psc refs unused --only-disabled-rules"
|
|
17
|
+
- type: dropdown
|
|
18
|
+
id: module
|
|
19
|
+
attributes:
|
|
20
|
+
label: Which area?
|
|
21
|
+
options:
|
|
22
|
+
- find / resolve
|
|
23
|
+
- dedup / merge
|
|
24
|
+
- refs / audit
|
|
25
|
+
- naming
|
|
26
|
+
- output / formats
|
|
27
|
+
- live / API
|
|
28
|
+
- other
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!-- Conventional Commit title, e.g. feat(dedup): merge service objects -->
|
|
2
|
+
|
|
3
|
+
## What & why
|
|
4
|
+
|
|
5
|
+
<!-- What does this change and why? Link the issue: Closes #N -->
|
|
6
|
+
|
|
7
|
+
## Safety
|
|
8
|
+
|
|
9
|
+
<!-- For any change to writes/merge/rename/apply: -->
|
|
10
|
+
- [ ] Dry-run remains the default; writes still require `--apply`.
|
|
11
|
+
- [ ] References are repointed before deletes/renames.
|
|
12
|
+
- [ ] Unsafe cases add a `blocker` rather than doing something surprising.
|
|
13
|
+
|
|
14
|
+
## Checklist
|
|
15
|
+
|
|
16
|
+
- [ ] `just test` green
|
|
17
|
+
- [ ] `just lint` clean (ruff + mypy --strict)
|
|
18
|
+
- [ ] Tests added for safety-critical paths
|
|
19
|
+
- [ ] Docs / CHANGELOG updated if user-facing
|
|
20
|
+
- [ ] `just sync-agents` run if `CLAUDE.md` changed
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
paths:
|
|
6
|
+
- "docs/**"
|
|
7
|
+
- "mkdocs.yml"
|
|
8
|
+
- ".github/workflows/docs.yml"
|
|
9
|
+
push:
|
|
10
|
+
tags:
|
|
11
|
+
- "v*"
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
pages: write
|
|
16
|
+
id-token: write
|
|
17
|
+
|
|
18
|
+
concurrency:
|
|
19
|
+
group: pages
|
|
20
|
+
cancel-in-progress: false
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
build:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- name: Install uv
|
|
28
|
+
uses: astral-sh/setup-uv@v3
|
|
29
|
+
with:
|
|
30
|
+
enable-cache: true
|
|
31
|
+
- name: Install Python
|
|
32
|
+
run: uv python install 3.12
|
|
33
|
+
- name: Sync deps (runtime + docs)
|
|
34
|
+
run: uv sync --frozen --group docs
|
|
35
|
+
- name: Build site (strict)
|
|
36
|
+
run: uv run mkdocs build --strict
|
|
37
|
+
- name: Upload Pages artifact
|
|
38
|
+
if: github.ref_type == 'tag'
|
|
39
|
+
uses: actions/upload-pages-artifact@v3
|
|
40
|
+
with:
|
|
41
|
+
path: site
|
|
42
|
+
|
|
43
|
+
deploy:
|
|
44
|
+
needs: build
|
|
45
|
+
if: github.ref_type == 'tag'
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
environment:
|
|
48
|
+
name: github-pages
|
|
49
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
50
|
+
steps:
|
|
51
|
+
- id: deployment
|
|
52
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,41 @@
|
|
|
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 psc tests
|
|
23
|
+
- name: Ruff format
|
|
24
|
+
run: uv run ruff format --check psc tests
|
|
25
|
+
- name: Mypy strict
|
|
26
|
+
run: uv run mypy --strict psc
|
|
27
|
+
|
|
28
|
+
agents-md-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
|
|
39
|
+
run: uv sync --frozen
|
|
40
|
+
- name: Check AGENTS.md is in sync with CLAUDE.md
|
|
41
|
+
run: uv run python scripts/sync_agents_md.py --check
|
|
@@ -0,0 +1,112 @@
|
|
|
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.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags:
|
|
8
|
+
- "v[0-9]*.[0-9]*.[0-9]*"
|
|
9
|
+
|
|
10
|
+
# Trusted publishing requires id-token: write so the OIDC token issued to
|
|
11
|
+
# pypa/gh-action-pypi-publish is signed.
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write # gh release create
|
|
14
|
+
id-token: write # PyPI trusted publishing OIDC
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: release
|
|
18
|
+
cancel-in-progress: false
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
release:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
# No `environment:` block — adding one changes the OIDC subject claim and
|
|
24
|
+
# would break a trusted-publisher binding registered against the
|
|
25
|
+
# no-environment form.
|
|
26
|
+
steps:
|
|
27
|
+
- name: Checkout (full history for changelog notes)
|
|
28
|
+
uses: actions/checkout@v4
|
|
29
|
+
with:
|
|
30
|
+
fetch-depth: 0
|
|
31
|
+
|
|
32
|
+
- name: Verify tag is on main
|
|
33
|
+
run: |
|
|
34
|
+
set -euo pipefail
|
|
35
|
+
git fetch origin main
|
|
36
|
+
if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
|
|
37
|
+
echo "Tag $GITHUB_REF_NAME points at $GITHUB_SHA which is not reachable from main." >&2
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
echo "Tag $GITHUB_REF_NAME ($GITHUB_SHA) is on main."
|
|
41
|
+
|
|
42
|
+
- name: Install uv
|
|
43
|
+
uses: astral-sh/setup-uv@v3
|
|
44
|
+
with:
|
|
45
|
+
enable-cache: true
|
|
46
|
+
|
|
47
|
+
- name: Install Python
|
|
48
|
+
run: uv python install 3.12
|
|
49
|
+
|
|
50
|
+
- name: Sync runtime deps (so the next step can import psc._version)
|
|
51
|
+
run: uv sync --frozen
|
|
52
|
+
|
|
53
|
+
- name: Verify tag matches package version
|
|
54
|
+
run: |
|
|
55
|
+
set -euo pipefail
|
|
56
|
+
tag_version="${GITHUB_REF_NAME#v}"
|
|
57
|
+
pkg_version="$(uv run python -c 'from psc._version import __version__; print(__version__)')"
|
|
58
|
+
if [ "$tag_version" != "$pkg_version" ]; then
|
|
59
|
+
echo "Tag $GITHUB_REF_NAME -> $tag_version does not match psc/_version.py $pkg_version" >&2
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
echo "Tag $GITHUB_REF_NAME matches package version $pkg_version"
|
|
63
|
+
|
|
64
|
+
- name: Build sdist + wheel
|
|
65
|
+
run: |
|
|
66
|
+
rm -rf dist
|
|
67
|
+
uv build --out-dir dist
|
|
68
|
+
ls -la dist/
|
|
69
|
+
|
|
70
|
+
- name: Verify wheel + sdist filenames match tag
|
|
71
|
+
run: |
|
|
72
|
+
set -euo pipefail
|
|
73
|
+
tag_version="${GITHUB_REF_NAME#v}"
|
|
74
|
+
if ! ls "dist/panorama_super_cli-${tag_version}-py3-none-any.whl" >/dev/null 2>&1; then
|
|
75
|
+
echo "Wheel for ${tag_version} not found in dist/" >&2; ls dist/ >&2; exit 1
|
|
76
|
+
fi
|
|
77
|
+
if ! ls "dist/panorama_super_cli-${tag_version}.tar.gz" >/dev/null 2>&1; then
|
|
78
|
+
echo "Sdist for ${tag_version} not found in dist/" >&2; ls dist/ >&2; exit 1
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
- name: Publish to PyPI (trusted publishing)
|
|
82
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
83
|
+
with:
|
|
84
|
+
packages-dir: dist
|
|
85
|
+
verbose: true
|
|
86
|
+
skip-existing: true
|
|
87
|
+
|
|
88
|
+
- name: Extract changelog section for release notes
|
|
89
|
+
run: |
|
|
90
|
+
set -euo pipefail
|
|
91
|
+
tag_version="${GITHUB_REF_NAME#v}"
|
|
92
|
+
awk -v ver="$tag_version" '
|
|
93
|
+
$0 ~ "^## v" ver " " { capture=1; print; next }
|
|
94
|
+
capture && /^## / { exit }
|
|
95
|
+
capture { print }
|
|
96
|
+
' CHANGELOG.md > /tmp/release-notes.md
|
|
97
|
+
if [ ! -s /tmp/release-notes.md ]; then
|
|
98
|
+
echo "Release notes for v${tag_version} not found in CHANGELOG.md" >&2
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
- name: Create GitHub Release
|
|
103
|
+
env:
|
|
104
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
105
|
+
run: |
|
|
106
|
+
set -euo pipefail
|
|
107
|
+
tag_version="${GITHUB_REF_NAME#v}"
|
|
108
|
+
gh release create "$GITHUB_REF_NAME" \
|
|
109
|
+
--title "$GITHUB_REF_NAME" \
|
|
110
|
+
--notes-file /tmp/release-notes.md \
|
|
111
|
+
"dist/panorama_super_cli-${tag_version}-py3-none-any.whl" \
|
|
112
|
+
"dist/panorama_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,38 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
*.egg
|
|
9
|
+
|
|
10
|
+
# venv / uv
|
|
11
|
+
.venv/
|
|
12
|
+
.python-version.local
|
|
13
|
+
|
|
14
|
+
# tooling caches
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
htmlcov/
|
|
20
|
+
coverage.xml
|
|
21
|
+
|
|
22
|
+
# mkdocs
|
|
23
|
+
site/
|
|
24
|
+
|
|
25
|
+
# editors / OS
|
|
26
|
+
.idea/
|
|
27
|
+
.vscode/
|
|
28
|
+
.DS_Store
|
|
29
|
+
|
|
30
|
+
# local scratch
|
|
31
|
+
/scratch/
|
|
32
|
+
*.local.*
|
|
33
|
+
|
|
34
|
+
# never commit real Panorama exports or secrets
|
|
35
|
+
*.panrc
|
|
36
|
+
secrets.*
|
|
37
|
+
*.config.xml
|
|
38
|
+
!tests/**/*.xml
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
args: [--maxkb=1024]
|
|
12
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
13
|
+
rev: v0.15.12
|
|
14
|
+
hooks:
|
|
15
|
+
- id: ruff
|
|
16
|
+
args: [--fix]
|
|
17
|
+
- id: ruff-format
|
|
18
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
19
|
+
rev: v1.20.2
|
|
20
|
+
hooks:
|
|
21
|
+
- id: mypy
|
|
22
|
+
additional_dependencies:
|
|
23
|
+
- pydantic>=2.7
|
|
24
|
+
- typer>=0.12
|
|
25
|
+
- rich>=13.7
|
|
26
|
+
- "ruamel.yaml>=0.18"
|
|
27
|
+
- structlog>=24.1
|
|
28
|
+
- platformdirs>=4.2
|
|
29
|
+
- types-PyYAML>=6.0
|
|
30
|
+
args: [--strict]
|
|
31
|
+
files: ^(psc|scripts)/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,97 @@
|
|
|
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 panorama-super-cli (for AI agents)
|
|
9
|
+
|
|
10
|
+
Contributor guide for AI agents (and humans) modifying this repo. The
|
|
11
|
+
*end-user* agent guide is the bundled Skill at
|
|
12
|
+
`skills/panorama-super-cli/SKILL.md` — that one is about *using* `psc`; this one
|
|
13
|
+
is about *changing* it.
|
|
14
|
+
|
|
15
|
+
## Architecture cheat sheet
|
|
16
|
+
|
|
17
|
+
The hard split is **backend (`psc/core`) vs frontend (`psc/cli`)**. A future web
|
|
18
|
+
UI would import `psc.core` directly and never touch `psc.cli`.
|
|
19
|
+
|
|
20
|
+
- `psc/core/models.py` — framework-free Pydantic domain model (`Address`,
|
|
21
|
+
`AddressGroup`, `Service`, `ServiceGroup`, `Tag`, `SecurityRule`, `NatRule`,
|
|
22
|
+
`Snapshot`, `Location`). The lingua franca; imports nothing from the rest.
|
|
23
|
+
- `psc/core/parse.py` — Panorama config XML → `Snapshot` (via `defusedxml`).
|
|
24
|
+
- `psc/core/normalize.py` — IP/value canonicalization + matching (the numeric
|
|
25
|
+
heart of `find` and `dedup`).
|
|
26
|
+
- `psc/core/refs.py` — the reference graph: where-used, unused (recursive),
|
|
27
|
+
dangling. Models PAN-OS name resolution (DG-local shadows shared).
|
|
28
|
+
- `psc/core/resolve.py` — `find` engine (IP/value/name → objects).
|
|
29
|
+
- `psc/core/dedup.py` — duplicate detection + safe merge planning.
|
|
30
|
+
- `psc/core/naming.py` — opt-in naming templates + reference-aware rename.
|
|
31
|
+
- `psc/core/changeset.py` — the inspectable mutation plan every write produces.
|
|
32
|
+
- `psc/core/setcmd.py` — render objects/changesets as PAN-OS `set` commands.
|
|
33
|
+
- `psc/core/apply_xml.py` — apply a `ChangeSet` to config XML (offline `--apply`).
|
|
34
|
+
- `psc/core/source.py` — `OfflineSource` (file) / `LiveSource` (pan-os-python).
|
|
35
|
+
- `psc/output/` — formatters (table/json/jsonl/yaml/csv/set) + error envelope.
|
|
36
|
+
- `psc/config/` — profiles + defaults (ruamel round-trip).
|
|
37
|
+
- `psc/cli/` — the Typer app; one thin command module per feature group.
|
|
38
|
+
|
|
39
|
+
The hard rule: **`psc/core/` imports nothing from `psc/cli/` or any UI
|
|
40
|
+
framework.** Engines return models; the CLI formats them. If you reach for
|
|
41
|
+
`typer`/`rich` inside `core/`, you're in the wrong layer.
|
|
42
|
+
|
|
43
|
+
Features are deliberately independent: `find_cmds`, `dedup_cmds`, `refs_cmds`,
|
|
44
|
+
`name_cmds` each map to one `core` engine and can be deleted without touching
|
|
45
|
+
the others. Reuse lives in `core` (models, refs, changeset, setcmd).
|
|
46
|
+
|
|
47
|
+
## Common commands
|
|
48
|
+
|
|
49
|
+
- `just sync` — install/refresh deps.
|
|
50
|
+
- `just test` — run all tests.
|
|
51
|
+
- `just lint` — ruff + mypy --strict.
|
|
52
|
+
- `just fix` — auto-fix ruff issues.
|
|
53
|
+
- `just psc <args>` — run the local CLI.
|
|
54
|
+
- `just sync-agents` — regenerate AGENTS.md from CLAUDE.md.
|
|
55
|
+
|
|
56
|
+
## Conventions
|
|
57
|
+
|
|
58
|
+
- Python 3.12+, full type annotations, `mypy --strict`.
|
|
59
|
+
- Pydantic v2 for all structured data.
|
|
60
|
+
- Conventional Commits; squash-merge; `main` is always releasable.
|
|
61
|
+
- TDD: write the failing test first. The safety-critical paths (merge
|
|
62
|
+
repointing, blockers, apply round-trip, shadow-rename refusal) MUST have tests.
|
|
63
|
+
- Comments explain non-obvious *why*, never *what*.
|
|
64
|
+
|
|
65
|
+
## Safety model (do not regress)
|
|
66
|
+
|
|
67
|
+
- **Dry-run is the default.** Mutating commands print a plan and exit without
|
|
68
|
+
writing unless `--apply` is passed.
|
|
69
|
+
- **Repoint before delete.** A merge/rename rewrites every referencing group,
|
|
70
|
+
security rule, and NAT rule *before* removing the object.
|
|
71
|
+
- **`ChangeSet.blockers` is a hard gate.** A non-empty `blockers` list means the
|
|
72
|
+
executor refuses to apply, even with `--apply`. Add a blocker rather than
|
|
73
|
+
silently doing something surprising (e.g. a cross-scope reference that can't
|
|
74
|
+
be repointed, or a shared-rename that would shadow a device-group object).
|
|
75
|
+
- **Offline `--apply` never overwrites the source export** — it writes to `--out`.
|
|
76
|
+
|
|
77
|
+
## Error contract
|
|
78
|
+
|
|
79
|
+
Expected failures raise `PscError(message, ErrorType.…)`. Exit codes are part
|
|
80
|
+
of the public contract (`psc/output/errors.py::EXIT_CODES`) — don't renumber
|
|
81
|
+
without a major bump. Machine output is never rich-wrapped (`soft_wrap=True`).
|
|
82
|
+
|
|
83
|
+
## Branching
|
|
84
|
+
|
|
85
|
+
`main` is protected; work on short-lived `feat/…`, `fix/…`, `docs/…` branches,
|
|
86
|
+
open a PR, get CI (`lint`, `test`) green, squash-merge. Releases are `vX.Y.Z`
|
|
87
|
+
*tags* on `main` (never branches); the tag triggers `release.yml` →
|
|
88
|
+
PyPI (trusted publishing) + the docs site.
|
|
89
|
+
|
|
90
|
+
Keep `psc/_version.py` and `pyproject.toml` `version` in agreement; the release
|
|
91
|
+
workflow validates the tag against `psc/_version.py`.
|
|
92
|
+
|
|
93
|
+
## Coordinating work on issues and PRs
|
|
94
|
+
|
|
95
|
+
When you start on an issue or PR, comment so other agents see it's claimed
|
|
96
|
+
(`gh issue comment <n> -b "…"`). Post terse updates on claim, milestones
|
|
97
|
+
(branch pushed, PR opened, CI green), blockers, and completion.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `panorama-super-cli` are documented here. The format is
|
|
4
|
+
based on [Keep a Changelog](https://keepachangelog.com/), and from v1.0.0 the
|
|
5
|
+
project will follow [Semantic Versioning](https://semver.org/). While on
|
|
6
|
+
`0.x`, minor versions may include breaking changes.
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## v0.1.0 — 2026-06-03
|
|
11
|
+
|
|
12
|
+
First public release. Agent-friendly Panorama object management, offline or live.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Offline + live sources.** Read an exported config with `--config file.xml`,
|
|
17
|
+
or a live Panorama via a profile (`psc profile add`). The same XML parser
|
|
18
|
+
feeds both paths.
|
|
19
|
+
- **`psc find ip`** — resolve an IP / CIDR / range / FQDN (or a `--file` list)
|
|
20
|
+
to the address objects that match it: exact, containing (broader), and
|
|
21
|
+
within (narrower), plus the address-groups that carry them.
|
|
22
|
+
- **`psc find object`** — locate any object by exact name across all kinds and
|
|
23
|
+
locations.
|
|
24
|
+
- **`psc dedup addresses|services`** — find objects that share an identical
|
|
25
|
+
value under different names (e.g. `10.0.0.10` as `h-web1` *and* `web-primary`).
|
|
26
|
+
- **`psc dedup merge`** — collapse one object into another, repointing every
|
|
27
|
+
group/security-rule/NAT reference *before* deleting it. Refuses (blocks)
|
|
28
|
+
value-mismatch merges and references that can't be safely repointed.
|
|
29
|
+
- **`psc refs used|unused|dangling`** — where-used pre-flight, recursive unused
|
|
30
|
+
detection (objects no rule reaches even through groups), and dangling-reference
|
|
31
|
+
audit.
|
|
32
|
+
- **`psc name lint|rename|apply`** — opt-in naming templates; report drift and
|
|
33
|
+
perform reference-aware renames that refuse the shared-vs-device-group shadow
|
|
34
|
+
collision.
|
|
35
|
+
- **Safety model.** Dry-run by default; `--apply` is the only path to a write.
|
|
36
|
+
Offline `--apply --out fixed.xml` produces a loadable, cleaned config without
|
|
37
|
+
touching the source export. `ChangeSet.blockers` is a hard refusal gate.
|
|
38
|
+
- **Output formats** `table, json, jsonl, yaml, csv, set` with a stable JSON
|
|
39
|
+
error envelope and typed exit codes; non-TTY stdout auto-switches to JSON.
|
|
40
|
+
`-o set` emits ready-to-paste PAN-OS `set` commands (member edits render as
|
|
41
|
+
delete-then-set, so they're idempotent rather than additive).
|
|
42
|
+
- **Agent Skill** bundled at `skills/panorama-super-cli/SKILL.md`.
|
|
43
|
+
|
|
44
|
+
### Known limitations (tracked for v0.2)
|
|
45
|
+
|
|
46
|
+
- Live `--apply` is not yet implemented; use `-o set` or offline `--apply`.
|
|
47
|
+
- Where-used covers address-groups, security rules, and NAT (match +
|
|
48
|
+
translation). PBF, decryption, authentication, QoS, and other rulebases are
|
|
49
|
+
not yet scanned.
|
|
50
|
+
- Nested device-group hierarchies are flattened to the leaf; only
|
|
51
|
+
device-group-shadows-shared inheritance is modelled.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Working on panorama-super-cli (for AI agents)
|
|
2
|
+
|
|
3
|
+
Contributor guide for AI agents (and humans) modifying this repo. The
|
|
4
|
+
*end-user* agent guide is the bundled Skill at
|
|
5
|
+
`skills/panorama-super-cli/SKILL.md` — that one is about *using* `psc`; this one
|
|
6
|
+
is about *changing* it.
|
|
7
|
+
|
|
8
|
+
## Architecture cheat sheet
|
|
9
|
+
|
|
10
|
+
The hard split is **backend (`psc/core`) vs frontend (`psc/cli`)**. A future web
|
|
11
|
+
UI would import `psc.core` directly and never touch `psc.cli`.
|
|
12
|
+
|
|
13
|
+
- `psc/core/models.py` — framework-free Pydantic domain model (`Address`,
|
|
14
|
+
`AddressGroup`, `Service`, `ServiceGroup`, `Tag`, `SecurityRule`, `NatRule`,
|
|
15
|
+
`Snapshot`, `Location`). The lingua franca; imports nothing from the rest.
|
|
16
|
+
- `psc/core/parse.py` — Panorama config XML → `Snapshot` (via `defusedxml`).
|
|
17
|
+
- `psc/core/normalize.py` — IP/value canonicalization + matching (the numeric
|
|
18
|
+
heart of `find` and `dedup`).
|
|
19
|
+
- `psc/core/refs.py` — the reference graph: where-used, unused (recursive),
|
|
20
|
+
dangling. Models PAN-OS name resolution (DG-local shadows shared).
|
|
21
|
+
- `psc/core/resolve.py` — `find` engine (IP/value/name → objects).
|
|
22
|
+
- `psc/core/dedup.py` — duplicate detection + safe merge planning.
|
|
23
|
+
- `psc/core/naming.py` — opt-in naming templates + reference-aware rename.
|
|
24
|
+
- `psc/core/changeset.py` — the inspectable mutation plan every write produces.
|
|
25
|
+
- `psc/core/setcmd.py` — render objects/changesets as PAN-OS `set` commands.
|
|
26
|
+
- `psc/core/apply_xml.py` — apply a `ChangeSet` to config XML (offline `--apply`).
|
|
27
|
+
- `psc/core/source.py` — `OfflineSource` (file) / `LiveSource` (pan-os-python).
|
|
28
|
+
- `psc/output/` — formatters (table/json/jsonl/yaml/csv/set) + error envelope.
|
|
29
|
+
- `psc/config/` — profiles + defaults (ruamel round-trip).
|
|
30
|
+
- `psc/cli/` — the Typer app; one thin command module per feature group.
|
|
31
|
+
|
|
32
|
+
The hard rule: **`psc/core/` imports nothing from `psc/cli/` or any UI
|
|
33
|
+
framework.** Engines return models; the CLI formats them. If you reach for
|
|
34
|
+
`typer`/`rich` inside `core/`, you're in the wrong layer.
|
|
35
|
+
|
|
36
|
+
Features are deliberately independent: `find_cmds`, `dedup_cmds`, `refs_cmds`,
|
|
37
|
+
`name_cmds` each map to one `core` engine and can be deleted without touching
|
|
38
|
+
the others. Reuse lives in `core` (models, refs, changeset, setcmd).
|
|
39
|
+
|
|
40
|
+
## Common commands
|
|
41
|
+
|
|
42
|
+
- `just sync` — install/refresh deps.
|
|
43
|
+
- `just test` — run all tests.
|
|
44
|
+
- `just lint` — ruff + mypy --strict.
|
|
45
|
+
- `just fix` — auto-fix ruff issues.
|
|
46
|
+
- `just psc <args>` — run the local CLI.
|
|
47
|
+
- `just sync-agents` — regenerate AGENTS.md from CLAUDE.md.
|
|
48
|
+
|
|
49
|
+
## Conventions
|
|
50
|
+
|
|
51
|
+
- Python 3.12+, full type annotations, `mypy --strict`.
|
|
52
|
+
- Pydantic v2 for all structured data.
|
|
53
|
+
- Conventional Commits; squash-merge; `main` is always releasable.
|
|
54
|
+
- TDD: write the failing test first. The safety-critical paths (merge
|
|
55
|
+
repointing, blockers, apply round-trip, shadow-rename refusal) MUST have tests.
|
|
56
|
+
- Comments explain non-obvious *why*, never *what*.
|
|
57
|
+
|
|
58
|
+
## Safety model (do not regress)
|
|
59
|
+
|
|
60
|
+
- **Dry-run is the default.** Mutating commands print a plan and exit without
|
|
61
|
+
writing unless `--apply` is passed.
|
|
62
|
+
- **Repoint before delete.** A merge/rename rewrites every referencing group,
|
|
63
|
+
security rule, and NAT rule *before* removing the object.
|
|
64
|
+
- **`ChangeSet.blockers` is a hard gate.** A non-empty `blockers` list means the
|
|
65
|
+
executor refuses to apply, even with `--apply`. Add a blocker rather than
|
|
66
|
+
silently doing something surprising (e.g. a cross-scope reference that can't
|
|
67
|
+
be repointed, or a shared-rename that would shadow a device-group object).
|
|
68
|
+
- **Offline `--apply` never overwrites the source export** — it writes to `--out`.
|
|
69
|
+
|
|
70
|
+
## Error contract
|
|
71
|
+
|
|
72
|
+
Expected failures raise `PscError(message, ErrorType.…)`. Exit codes are part
|
|
73
|
+
of the public contract (`psc/output/errors.py::EXIT_CODES`) — don't renumber
|
|
74
|
+
without a major bump. Machine output is never rich-wrapped (`soft_wrap=True`).
|
|
75
|
+
|
|
76
|
+
## Branching
|
|
77
|
+
|
|
78
|
+
`main` is protected; work on short-lived `feat/…`, `fix/…`, `docs/…` branches,
|
|
79
|
+
open a PR, get CI (`lint`, `test`) green, squash-merge. Releases are `vX.Y.Z`
|
|
80
|
+
*tags* on `main` (never branches); the tag triggers `release.yml` →
|
|
81
|
+
PyPI (trusted publishing) + the docs site.
|
|
82
|
+
|
|
83
|
+
Keep `psc/_version.py` and `pyproject.toml` `version` in agreement; the release
|
|
84
|
+
workflow validates the tag against `psc/_version.py`.
|
|
85
|
+
|
|
86
|
+
## Coordinating work on issues and PRs
|
|
87
|
+
|
|
88
|
+
When you start on an issue or PR, comment so other agents see it's claimed
|
|
89
|
+
(`gh issue comment <n> -b "…"`). Post terse updates on claim, milestones
|
|
90
|
+
(branch pushed, PR opened, CI green), blockers, and completion.
|