cmtrl-netscope 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.
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .ruff_cache/
8
+ netscope-*.json
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ayoub Alshammari (cmtrl)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include main.py requirements.txt .gitignore
2
+ recursive-include tests *.py
3
+ recursive-include docs *.md
@@ -0,0 +1,160 @@
1
+ Metadata-Version: 2.4
2
+ Name: cmtrl-netscope
3
+ Version: 1.0.0
4
+ Summary: A Python CLI for checking DNS records, TCP ports, and HTTP headers
5
+ Author: Ayoub Alshammari
6
+ License-Expression: MIT
7
+ Project-URL: GitHub profile, https://github.com/cmtrl
8
+ Project-URL: Repository, https://github.com/cmtrl/netscope
9
+ Project-URL: Issues, https://github.com/cmtrl/netscope/issues
10
+ Project-URL: Documentation, https://github.com/cmtrl/netscope/blob/main/docs/REFERENCE.md
11
+ Classifier: Environment :: Console
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: System :: Networking
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: aiohttp<4.0,>=3.12.14
18
+ Requires-Dist: dnspython<3.0,>=2.7.0
19
+ Requires-Dist: rich<15.0,>=13.9.4
20
+ Dynamic: license-file
21
+
22
+ # NetScope
23
+
24
+ A Python CLI for checking DNS records, common TCP ports, and HTTP headers.
25
+
26
+ By **Ayoub Alshammari** · [@cmtrl](https://github.com/cmtrl)
27
+
28
+ ```text
29
+ _ _ _ ____
30
+ | \ | | ___| |_/ ___| ___ ___ _ __ ___
31
+ | \| |/ _ \ __\___ \ / __/ _ \| '_ \ / _ \
32
+ | |\ | __/ |_ ___) | (_| (_) | |_) | __/
33
+ |_| \_|\___|\__|____/ \___\___/| .__/ \___|
34
+ |_|
35
+ ```
36
+
37
+ I'm using this project to work on Python networking, async code, and CLI design.
38
+ It's a starting point I plan to keep improving as I learn more about how these
39
+ checks work.
40
+
41
+ ## What it does
42
+
43
+ - Looks up A, AAAA, MX, TXT, and NS records concurrently.
44
+ - Checks TCP ports `21`, `22`, `80`, `443`, `8080`, and `8443`.
45
+ - Fetches HTTP and HTTPS response headers.
46
+ - Checks for HSTS, CSP, and X-Frame-Options.
47
+ - Shows technology hints from headers such as `Server` and `X-Powered-By`.
48
+ - Displays results in the terminal and optionally saves them as JSON.
49
+
50
+ The checks run with `asyncio`, `aiohttp`, and `dnspython`. The terminal output
51
+ uses `rich`.
52
+
53
+ The package is being prepared for PyPI as `cmtrl-netscope`. The command stays
54
+ `netscope`. See the [publishing guide](https://github.com/cmtrl/netscope/blob/main/docs/PUBLISHING.md)
55
+ for the release setup.
56
+
57
+ ## Setup
58
+
59
+ Requires Python **3.10 or newer**. No root privileges needed.
60
+
61
+ Download the source, open its folder, then run:
62
+
63
+ ```bash
64
+ python -m venv .venv
65
+ source .venv/bin/activate
66
+ python -m pip install -r requirements.txt
67
+ ```
68
+
69
+ On Windows PowerShell, activate with `.venv\Scripts\Activate.ps1` instead.
70
+ Use `python3` in place of `python` if that's how Python is installed on your system.
71
+
72
+ ## Usage
73
+
74
+ ```bash
75
+ python main.py -t example.com
76
+ ```
77
+
78
+ Save a JSON report:
79
+
80
+ ```bash
81
+ python main.py -t example.com -o json --output-file report.json
82
+ ```
83
+
84
+ Set a shorter timeout or change the number of concurrent TCP checks:
85
+
86
+ ```bash
87
+ python main.py -t example.com --timeout 3 --concurrency 12
88
+ ```
89
+
90
+ IP addresses work too. Forward DNS enumeration is skipped for IP targets:
91
+
92
+ ```bash
93
+ python main.py -t 127.0.0.1
94
+ python main.py -t ::1
95
+ ```
96
+
97
+ Use a bare hostname or IP address, without `https://`, a path, or a port number.
98
+
99
+ ```bash
100
+ python main.py --help
101
+ ```
102
+
103
+ You can also install the command in your virtual environment:
104
+
105
+ ```bash
106
+ python -m pip install .
107
+ netscope -t example.com
108
+ # Or: python -m netscope -t example.com
109
+ ```
110
+
111
+ ## A few things to know
112
+
113
+ Use this on systems you own or have permission to check.
114
+
115
+ An open port means a TCP connection succeeded. It doesn't confirm which service
116
+ is running. A missing security header isn't automatically a vulnerability, and
117
+ technology headers can be wrong.
118
+
119
+ HTTP checks use ports 80 and 443. Ports 8080 and 8443 get TCP checks only.
120
+ Redirects are shown without being followed, and HTTPS certificate verification
121
+ stays enabled. A failed check doesn't discard results from the other checks.
122
+
123
+ More detail on result states, timeouts, proxies, JSON fields, and exit codes is
124
+ in [docs/REFERENCE.md](https://github.com/cmtrl/netscope/blob/main/docs/REFERENCE.md).
125
+
126
+ ## Working on the code
127
+
128
+ ```text
129
+ main.py entry point
130
+ netscope/cli.py arguments and terminal output
131
+ netscope/scanner.py DNS, TCP, and HTTP checks
132
+ netscope/utils.py input validation and JSON export
133
+ tests/ tests using local servers and simulated failures
134
+ ```
135
+
136
+ Run the tests:
137
+
138
+ ```bash
139
+ python -m unittest discover -s tests -v
140
+ ```
141
+
142
+ Optional development tools:
143
+
144
+ ```bash
145
+ python -m pip install ruff build
146
+ ruff check .
147
+ python -m build
148
+ ```
149
+
150
+ Some things I'd like to work on next:
151
+
152
+ - Letting the user choose which ports to check.
153
+ - Making the output easier to skim when a host has several IP addresses.
154
+ - Adding more tests as I change the scanner.
155
+
156
+ If you find a bug or have a suggestion, an issue or pull request is welcome.
157
+
158
+ ## License
159
+
160
+ [MIT](https://github.com/cmtrl/netscope/blob/main/LICENSE) · Copyright (c) 2026 Ayoub Alshammari (cmtrl).
@@ -0,0 +1,139 @@
1
+ # NetScope
2
+
3
+ A Python CLI for checking DNS records, common TCP ports, and HTTP headers.
4
+
5
+ By **Ayoub Alshammari** · [@cmtrl](https://github.com/cmtrl)
6
+
7
+ ```text
8
+ _ _ _ ____
9
+ | \ | | ___| |_/ ___| ___ ___ _ __ ___
10
+ | \| |/ _ \ __\___ \ / __/ _ \| '_ \ / _ \
11
+ | |\ | __/ |_ ___) | (_| (_) | |_) | __/
12
+ |_| \_|\___|\__|____/ \___\___/| .__/ \___|
13
+ |_|
14
+ ```
15
+
16
+ I'm using this project to work on Python networking, async code, and CLI design.
17
+ It's a starting point I plan to keep improving as I learn more about how these
18
+ checks work.
19
+
20
+ ## What it does
21
+
22
+ - Looks up A, AAAA, MX, TXT, and NS records concurrently.
23
+ - Checks TCP ports `21`, `22`, `80`, `443`, `8080`, and `8443`.
24
+ - Fetches HTTP and HTTPS response headers.
25
+ - Checks for HSTS, CSP, and X-Frame-Options.
26
+ - Shows technology hints from headers such as `Server` and `X-Powered-By`.
27
+ - Displays results in the terminal and optionally saves them as JSON.
28
+
29
+ The checks run with `asyncio`, `aiohttp`, and `dnspython`. The terminal output
30
+ uses `rich`.
31
+
32
+ The package is being prepared for PyPI as `cmtrl-netscope`. The command stays
33
+ `netscope`. See the [publishing guide](https://github.com/cmtrl/netscope/blob/main/docs/PUBLISHING.md)
34
+ for the release setup.
35
+
36
+ ## Setup
37
+
38
+ Requires Python **3.10 or newer**. No root privileges needed.
39
+
40
+ Download the source, open its folder, then run:
41
+
42
+ ```bash
43
+ python -m venv .venv
44
+ source .venv/bin/activate
45
+ python -m pip install -r requirements.txt
46
+ ```
47
+
48
+ On Windows PowerShell, activate with `.venv\Scripts\Activate.ps1` instead.
49
+ Use `python3` in place of `python` if that's how Python is installed on your system.
50
+
51
+ ## Usage
52
+
53
+ ```bash
54
+ python main.py -t example.com
55
+ ```
56
+
57
+ Save a JSON report:
58
+
59
+ ```bash
60
+ python main.py -t example.com -o json --output-file report.json
61
+ ```
62
+
63
+ Set a shorter timeout or change the number of concurrent TCP checks:
64
+
65
+ ```bash
66
+ python main.py -t example.com --timeout 3 --concurrency 12
67
+ ```
68
+
69
+ IP addresses work too. Forward DNS enumeration is skipped for IP targets:
70
+
71
+ ```bash
72
+ python main.py -t 127.0.0.1
73
+ python main.py -t ::1
74
+ ```
75
+
76
+ Use a bare hostname or IP address, without `https://`, a path, or a port number.
77
+
78
+ ```bash
79
+ python main.py --help
80
+ ```
81
+
82
+ You can also install the command in your virtual environment:
83
+
84
+ ```bash
85
+ python -m pip install .
86
+ netscope -t example.com
87
+ # Or: python -m netscope -t example.com
88
+ ```
89
+
90
+ ## A few things to know
91
+
92
+ Use this on systems you own or have permission to check.
93
+
94
+ An open port means a TCP connection succeeded. It doesn't confirm which service
95
+ is running. A missing security header isn't automatically a vulnerability, and
96
+ technology headers can be wrong.
97
+
98
+ HTTP checks use ports 80 and 443. Ports 8080 and 8443 get TCP checks only.
99
+ Redirects are shown without being followed, and HTTPS certificate verification
100
+ stays enabled. A failed check doesn't discard results from the other checks.
101
+
102
+ More detail on result states, timeouts, proxies, JSON fields, and exit codes is
103
+ in [docs/REFERENCE.md](https://github.com/cmtrl/netscope/blob/main/docs/REFERENCE.md).
104
+
105
+ ## Working on the code
106
+
107
+ ```text
108
+ main.py entry point
109
+ netscope/cli.py arguments and terminal output
110
+ netscope/scanner.py DNS, TCP, and HTTP checks
111
+ netscope/utils.py input validation and JSON export
112
+ tests/ tests using local servers and simulated failures
113
+ ```
114
+
115
+ Run the tests:
116
+
117
+ ```bash
118
+ python -m unittest discover -s tests -v
119
+ ```
120
+
121
+ Optional development tools:
122
+
123
+ ```bash
124
+ python -m pip install ruff build
125
+ ruff check .
126
+ python -m build
127
+ ```
128
+
129
+ Some things I'd like to work on next:
130
+
131
+ - Letting the user choose which ports to check.
132
+ - Making the output easier to skim when a host has several IP addresses.
133
+ - Adding more tests as I change the scanner.
134
+
135
+ If you find a bug or have a suggestion, an issue or pull request is welcome.
136
+
137
+ ## License
138
+
139
+ [MIT](https://github.com/cmtrl/netscope/blob/main/LICENSE) · Copyright (c) 2026 Ayoub Alshammari (cmtrl).
@@ -0,0 +1,160 @@
1
+ Metadata-Version: 2.4
2
+ Name: cmtrl-netscope
3
+ Version: 1.0.0
4
+ Summary: A Python CLI for checking DNS records, TCP ports, and HTTP headers
5
+ Author: Ayoub Alshammari
6
+ License-Expression: MIT
7
+ Project-URL: GitHub profile, https://github.com/cmtrl
8
+ Project-URL: Repository, https://github.com/cmtrl/netscope
9
+ Project-URL: Issues, https://github.com/cmtrl/netscope/issues
10
+ Project-URL: Documentation, https://github.com/cmtrl/netscope/blob/main/docs/REFERENCE.md
11
+ Classifier: Environment :: Console
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: System :: Networking
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: aiohttp<4.0,>=3.12.14
18
+ Requires-Dist: dnspython<3.0,>=2.7.0
19
+ Requires-Dist: rich<15.0,>=13.9.4
20
+ Dynamic: license-file
21
+
22
+ # NetScope
23
+
24
+ A Python CLI for checking DNS records, common TCP ports, and HTTP headers.
25
+
26
+ By **Ayoub Alshammari** · [@cmtrl](https://github.com/cmtrl)
27
+
28
+ ```text
29
+ _ _ _ ____
30
+ | \ | | ___| |_/ ___| ___ ___ _ __ ___
31
+ | \| |/ _ \ __\___ \ / __/ _ \| '_ \ / _ \
32
+ | |\ | __/ |_ ___) | (_| (_) | |_) | __/
33
+ |_| \_|\___|\__|____/ \___\___/| .__/ \___|
34
+ |_|
35
+ ```
36
+
37
+ I'm using this project to work on Python networking, async code, and CLI design.
38
+ It's a starting point I plan to keep improving as I learn more about how these
39
+ checks work.
40
+
41
+ ## What it does
42
+
43
+ - Looks up A, AAAA, MX, TXT, and NS records concurrently.
44
+ - Checks TCP ports `21`, `22`, `80`, `443`, `8080`, and `8443`.
45
+ - Fetches HTTP and HTTPS response headers.
46
+ - Checks for HSTS, CSP, and X-Frame-Options.
47
+ - Shows technology hints from headers such as `Server` and `X-Powered-By`.
48
+ - Displays results in the terminal and optionally saves them as JSON.
49
+
50
+ The checks run with `asyncio`, `aiohttp`, and `dnspython`. The terminal output
51
+ uses `rich`.
52
+
53
+ The package is being prepared for PyPI as `cmtrl-netscope`. The command stays
54
+ `netscope`. See the [publishing guide](https://github.com/cmtrl/netscope/blob/main/docs/PUBLISHING.md)
55
+ for the release setup.
56
+
57
+ ## Setup
58
+
59
+ Requires Python **3.10 or newer**. No root privileges needed.
60
+
61
+ Download the source, open its folder, then run:
62
+
63
+ ```bash
64
+ python -m venv .venv
65
+ source .venv/bin/activate
66
+ python -m pip install -r requirements.txt
67
+ ```
68
+
69
+ On Windows PowerShell, activate with `.venv\Scripts\Activate.ps1` instead.
70
+ Use `python3` in place of `python` if that's how Python is installed on your system.
71
+
72
+ ## Usage
73
+
74
+ ```bash
75
+ python main.py -t example.com
76
+ ```
77
+
78
+ Save a JSON report:
79
+
80
+ ```bash
81
+ python main.py -t example.com -o json --output-file report.json
82
+ ```
83
+
84
+ Set a shorter timeout or change the number of concurrent TCP checks:
85
+
86
+ ```bash
87
+ python main.py -t example.com --timeout 3 --concurrency 12
88
+ ```
89
+
90
+ IP addresses work too. Forward DNS enumeration is skipped for IP targets:
91
+
92
+ ```bash
93
+ python main.py -t 127.0.0.1
94
+ python main.py -t ::1
95
+ ```
96
+
97
+ Use a bare hostname or IP address, without `https://`, a path, or a port number.
98
+
99
+ ```bash
100
+ python main.py --help
101
+ ```
102
+
103
+ You can also install the command in your virtual environment:
104
+
105
+ ```bash
106
+ python -m pip install .
107
+ netscope -t example.com
108
+ # Or: python -m netscope -t example.com
109
+ ```
110
+
111
+ ## A few things to know
112
+
113
+ Use this on systems you own or have permission to check.
114
+
115
+ An open port means a TCP connection succeeded. It doesn't confirm which service
116
+ is running. A missing security header isn't automatically a vulnerability, and
117
+ technology headers can be wrong.
118
+
119
+ HTTP checks use ports 80 and 443. Ports 8080 and 8443 get TCP checks only.
120
+ Redirects are shown without being followed, and HTTPS certificate verification
121
+ stays enabled. A failed check doesn't discard results from the other checks.
122
+
123
+ More detail on result states, timeouts, proxies, JSON fields, and exit codes is
124
+ in [docs/REFERENCE.md](https://github.com/cmtrl/netscope/blob/main/docs/REFERENCE.md).
125
+
126
+ ## Working on the code
127
+
128
+ ```text
129
+ main.py entry point
130
+ netscope/cli.py arguments and terminal output
131
+ netscope/scanner.py DNS, TCP, and HTTP checks
132
+ netscope/utils.py input validation and JSON export
133
+ tests/ tests using local servers and simulated failures
134
+ ```
135
+
136
+ Run the tests:
137
+
138
+ ```bash
139
+ python -m unittest discover -s tests -v
140
+ ```
141
+
142
+ Optional development tools:
143
+
144
+ ```bash
145
+ python -m pip install ruff build
146
+ ruff check .
147
+ python -m build
148
+ ```
149
+
150
+ Some things I'd like to work on next:
151
+
152
+ - Letting the user choose which ports to check.
153
+ - Making the output easier to skim when a host has several IP addresses.
154
+ - Adding more tests as I change the scanner.
155
+
156
+ If you find a bug or have a suggestion, an issue or pull request is welcome.
157
+
158
+ ## License
159
+
160
+ [MIT](https://github.com/cmtrl/netscope/blob/main/LICENSE) · Copyright (c) 2026 Ayoub Alshammari (cmtrl).
@@ -0,0 +1,24 @@
1
+ .gitignore
2
+ LICENSE
3
+ MANIFEST.in
4
+ README.md
5
+ main.py
6
+ pyproject.toml
7
+ requirements.txt
8
+ cmtrl_netscope.egg-info/PKG-INFO
9
+ cmtrl_netscope.egg-info/SOURCES.txt
10
+ cmtrl_netscope.egg-info/dependency_links.txt
11
+ cmtrl_netscope.egg-info/entry_points.txt
12
+ cmtrl_netscope.egg-info/requires.txt
13
+ cmtrl_netscope.egg-info/top_level.txt
14
+ docs/DEMO.md
15
+ docs/PUBLISHING.md
16
+ docs/REFERENCE.md
17
+ netscope/__init__.py
18
+ netscope/__main__.py
19
+ netscope/cli.py
20
+ netscope/scanner.py
21
+ netscope/utils.py
22
+ tests/test_cli.py
23
+ tests/test_scanner.py
24
+ tests/test_utils.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ netscope = netscope.cli:main
@@ -0,0 +1,3 @@
1
+ aiohttp<4.0,>=3.12.14
2
+ dnspython<3.0,>=2.7.0
3
+ rich<15.0,>=13.9.4
@@ -0,0 +1,59 @@
1
+ # A short NetScope demo
2
+
3
+ Aim for about 25 seconds. Use a large terminal font and a clean window.
4
+ Keep notifications and unrelated windows out of the recording.
5
+
6
+ ## Commands
7
+
8
+ Until the PyPI release is live, install from the GitHub source in a virtual
9
+ environment:
10
+
11
+ ```bash
12
+ git clone https://github.com/cmtrl/netscope.git
13
+ cd netscope
14
+ python -m venv .venv
15
+ source .venv/bin/activate
16
+ python -m pip install .
17
+ ```
18
+
19
+ For a repeatable demo, start a small server in a second terminal:
20
+
21
+ ```bash
22
+ python -m http.server 8080 --bind 127.0.0.1
23
+ ```
24
+
25
+ Record this in your main terminal:
26
+
27
+ ```bash
28
+ netscope --version
29
+ netscope -t 127.0.0.1 --timeout 2 -o json --output-file demo.json
30
+ python -m json.tool demo.json
31
+ ```
32
+
33
+ This shows port 8080 open and a JSON report. Forward DNS is skipped for an IP
34
+ address, and HTTP analysis uses 80/443, so the server on 8080 does not produce
35
+ HTTP header results. Say that if you explain this demo.
36
+
37
+ For a fuller demo, use a domain you own or have permission to scan in place of
38
+ `127.0.0.1`. A failed request remains visible in the output; keep it in the clip.
39
+
40
+ ## Shot list
41
+
42
+ - **0–4 seconds:** show the command and target.
43
+ - **4–14 seconds:** run it and pause on the results.
44
+ - **14–20 seconds:** show the JSON export or one interesting result.
45
+ - **20–25 seconds:** show `github.com/cmtrl/netscope`.
46
+
47
+ OBS or your operating system's screen recorder can capture the terminal. Export
48
+ an MP4. Check that the text is readable at phone size before posting.
49
+
50
+ ## Post text
51
+
52
+ > First public project: NetScope, a Python CLI for DNS records, TCP port checks,
53
+ > and HTTP headers. I'm using it to learn async Python and networking, and I'll
54
+ > keep improving it as I go. Feedback welcome.
55
+ >
56
+ > https://github.com/cmtrl/netscope
57
+
58
+ After PyPI publishing succeeds, you can add `pip install cmtrl-netscope` to the
59
+ post. Avoid using it as an installation claim before the release exists.
@@ -0,0 +1,59 @@
1
+ # Publishing NetScope
2
+
3
+ The PyPI distribution name is `cmtrl-netscope`. The CLI command and Python
4
+ module are both still `netscope`.
5
+
6
+ `netscope-cli` belongs to another project on PyPI. A package name that returns
7
+ 404 is not reserved or guaranteed to be available until PyPI accepts the upload.
8
+
9
+ ## One-time account setup
10
+
11
+ Sign in to your own [PyPI account](https://pypi.org/account/publishing/).
12
+ If you need to register, complete PyPI's account verification and 2FA setup first.
13
+
14
+ Under **Add a new pending publisher**, select GitHub and enter:
15
+
16
+ | Field | Value |
17
+ | --- | --- |
18
+ | PyPI project name | `cmtrl-netscope` |
19
+ | Owner | `cmtrl` |
20
+ | Repository name | `netscope` |
21
+ | Workflow name | `publish.yml` |
22
+ | Environment name | `pypi` |
23
+
24
+ Save the pending publisher. It creates the PyPI project on the first successful
25
+ upload. No API token needs to be copied into chat or GitHub secrets.
26
+
27
+ This follows [PyPI's pending publisher setup](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/).
28
+
29
+ ## Publish
30
+
31
+ In GitHub, open **Actions → Publish to PyPI → Run workflow** and select `main`.
32
+ The workflow tests the code, builds and checks both distributions, then publishes
33
+ using GitHub's identity. It does not run automatically on ordinary commits.
34
+
35
+ After a successful upload, test installation in a fresh virtual environment:
36
+
37
+ ```bash
38
+ python -m venv .venv-release
39
+ source .venv-release/bin/activate
40
+ python -m pip install cmtrl-netscope
41
+ netscope --version
42
+ netscope --help
43
+ ```
44
+
45
+ Update the README to show the PyPI installation command after the release is live.
46
+
47
+ ## Later releases
48
+
49
+ Bump the version in both `pyproject.toml` and `netscope/utils.py`, commit the
50
+ changes, and run the workflow again. PyPI does not allow replacing an uploaded
51
+ distribution file with another file of the same name.
52
+
53
+ For a local packaging check:
54
+
55
+ ```bash
56
+ python -m pip install build twine
57
+ python -m build
58
+ python -m twine check --strict dist/*
59
+ ```