safereach 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.
- safereach-0.1.0/.github/workflows/ci.yml +43 -0
- safereach-0.1.0/.github/workflows/publish.yml +125 -0
- safereach-0.1.0/.gitignore +14 -0
- safereach-0.1.0/LICENSE +21 -0
- safereach-0.1.0/PKG-INFO +546 -0
- safereach-0.1.0/README.md +519 -0
- safereach-0.1.0/config/commands.yaml +586 -0
- safereach-0.1.0/config/hosts.example.yaml +75 -0
- safereach-0.1.0/pyproject.toml +63 -0
- safereach-0.1.0/shim/build.py +145 -0
- safereach-0.1.0/shim/install.sh +163 -0
- safereach-0.1.0/shim/shim_main.py +571 -0
- safereach-0.1.0/src/safereach/__init__.py +3 -0
- safereach-0.1.0/src/safereach/__main__.py +7 -0
- safereach-0.1.0/src/safereach/audit.py +99 -0
- safereach-0.1.0/src/safereach/cli.py +1884 -0
- safereach-0.1.0/src/safereach/config.py +292 -0
- safereach-0.1.0/src/safereach/discovery.py +212 -0
- safereach-0.1.0/src/safereach/install/__init__.py +0 -0
- safereach-0.1.0/src/safereach/install/adapters.py +353 -0
- safereach-0.1.0/src/safereach/naming.py +249 -0
- safereach-0.1.0/src/safereach/redact.py +297 -0
- safereach-0.1.0/src/safereach/server.py +635 -0
- safereach-0.1.0/src/safereach/ssh.py +245 -0
- safereach-0.1.0/src/safereach/validator.py +905 -0
- safereach-0.1.0/src/safereach/versioning.py +54 -0
- safereach-0.1.0/tests/conftest.py +273 -0
- safereach-0.1.0/tests/test_canary.py +151 -0
- safereach-0.1.0/tests/test_config.py +120 -0
- safereach-0.1.0/tests/test_discovery.py +175 -0
- safereach-0.1.0/tests/test_enroll.py +275 -0
- safereach-0.1.0/tests/test_install.py +211 -0
- safereach-0.1.0/tests/test_kubernetes.py +102 -0
- safereach-0.1.0/tests/test_naming.py +240 -0
- safereach-0.1.0/tests/test_redact.py +125 -0
- safereach-0.1.0/tests/test_rename.py +256 -0
- safereach-0.1.0/tests/test_secrets.py +131 -0
- safereach-0.1.0/tests/test_shim.py +277 -0
- safereach-0.1.0/tests/test_spec_lint.py +198 -0
- safereach-0.1.0/tests/test_stdio_clean.py +289 -0
- safereach-0.1.0/tests/test_validator_accept.py +107 -0
- safereach-0.1.0/tests/test_validator_attacks.py +106 -0
- safereach-0.1.0/uv.lock +910 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push: { branches: [main] }
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
strategy:
|
|
11
|
+
matrix:
|
|
12
|
+
python: ["3.11", "3.12", "3.13"]
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: astral-sh/setup-uv@v5
|
|
16
|
+
- run: uv python install ${{ matrix.python }}
|
|
17
|
+
- run: uv venv --python ${{ matrix.python }}
|
|
18
|
+
- run: uv pip install -e ".[dev]"
|
|
19
|
+
# The spec linter runs first: it fails the build if the allowlist ever permits a
|
|
20
|
+
# mutating verb, which is cheaper to learn before the rest of the suite runs.
|
|
21
|
+
- run: uv run pytest tests/test_spec_lint.py -q
|
|
22
|
+
- run: uv run pytest -q
|
|
23
|
+
- run: uv run ruff check .
|
|
24
|
+
- run: uv run ruff format --check .
|
|
25
|
+
- run: uv run python shim/build.py
|
|
26
|
+
|
|
27
|
+
# A single stable check name to require in branch protection.
|
|
28
|
+
#
|
|
29
|
+
# The matrix above produces `test (3.11)`, `test (3.12)`, `test (3.13)` — three names
|
|
30
|
+
# that change whenever the matrix does, and a required check that no longer exists
|
|
31
|
+
# silently stops gating anything. This job is one name that never changes.
|
|
32
|
+
#
|
|
33
|
+
# `if: always()` matters: without it, a skipped or failed matrix leg would leave this
|
|
34
|
+
# job skipped, and GitHub treats a skipped required check as passing.
|
|
35
|
+
ci-ok:
|
|
36
|
+
needs: test
|
|
37
|
+
if: always()
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
steps:
|
|
40
|
+
- name: All matrix legs must have succeeded
|
|
41
|
+
run: |
|
|
42
|
+
echo "test result: ${{ needs.test.result }}"
|
|
43
|
+
[ "${{ needs.test.result }}" = "success" ] || exit 1
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
# Trusted Publishing (OIDC) — no API token is stored anywhere. GitHub mints a
|
|
4
|
+
# short-lived identity token for this workflow, and PyPI accepts it only for the exact
|
|
5
|
+
# repository, workflow filename and environment configured on the PyPI side.
|
|
6
|
+
#
|
|
7
|
+
# That is why the `environment:` key below matters: PyPI checks it. If it does not match
|
|
8
|
+
# the "Environment name" on the trusted publisher, the upload is rejected — which is the
|
|
9
|
+
# point. It means a workflow added to some other branch cannot publish.
|
|
10
|
+
|
|
11
|
+
# Publishing is a deliberate act, run from `main`.
|
|
12
|
+
#
|
|
13
|
+
# The environment's deployment-branch rule is evaluated against the ref that triggered
|
|
14
|
+
# the run. A GitHub Release runs on the TAG ref, not on `main` — so an environment
|
|
15
|
+
# restricted to `main` would block every release with a fairly opaque message. Triggering
|
|
16
|
+
# from workflow_dispatch on `main` keeps the trigger and the rule in agreement.
|
|
17
|
+
#
|
|
18
|
+
# Cut the GitHub Release afterwards for the changelog; it no longer drives publishing.
|
|
19
|
+
on:
|
|
20
|
+
workflow_dispatch:
|
|
21
|
+
inputs:
|
|
22
|
+
target:
|
|
23
|
+
description: "Where to publish"
|
|
24
|
+
required: true
|
|
25
|
+
default: pypi
|
|
26
|
+
type: choice
|
|
27
|
+
options: [pypi, testpypi]
|
|
28
|
+
|
|
29
|
+
permissions:
|
|
30
|
+
contents: read
|
|
31
|
+
|
|
32
|
+
jobs:
|
|
33
|
+
build:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: astral-sh/setup-uv@v5
|
|
38
|
+
|
|
39
|
+
# The tag and pyproject version must agree. Tagging v0.2.0 while pyproject still
|
|
40
|
+
# says 0.1.0 otherwise builds and uploads 0.1.0 under a 0.2.0 release — or fails
|
|
41
|
+
# much later with "file already exists", which reads like an infrastructure problem
|
|
42
|
+
# rather than the version bump you forgot.
|
|
43
|
+
- name: Must be publishing from main
|
|
44
|
+
run: |
|
|
45
|
+
[ "$GITHUB_REF_NAME" = "main" ] || {
|
|
46
|
+
echo "::error::publish only from main (got $GITHUB_REF_NAME)"
|
|
47
|
+
exit 1
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# PyPI versions are immutable: a published 0.1.0 can never be replaced, only
|
|
51
|
+
# yanked. Failing here is far cheaper than burning a version number.
|
|
52
|
+
- name: Version must not already be on PyPI
|
|
53
|
+
if: inputs.target == 'pypi'
|
|
54
|
+
run: |
|
|
55
|
+
PKG=$(grep -m1 '^version = ' pyproject.toml | cut -d'"' -f2)
|
|
56
|
+
CODE=$(curl -s -o /dev/null -w '%{http_code}' "https://pypi.org/pypi/safereach/$PKG/json")
|
|
57
|
+
[ "$CODE" = "404" ] || {
|
|
58
|
+
echo "::error::safereach $PKG is already published — bump the version"
|
|
59
|
+
exit 1
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
- run: uv python install 3.11
|
|
63
|
+
- run: uv venv --python 3.11
|
|
64
|
+
- run: uv pip install -e ".[dev]"
|
|
65
|
+
|
|
66
|
+
# Never ship a release that fails its own tests. The spec linter runs first: it
|
|
67
|
+
# fails the build if the allowlist ever permits a mutating verb.
|
|
68
|
+
- name: Spec linter
|
|
69
|
+
run: uv run pytest tests/test_spec_lint.py -q
|
|
70
|
+
- name: Full suite
|
|
71
|
+
run: uv run pytest -q
|
|
72
|
+
- name: Lint
|
|
73
|
+
run: uv run ruff check .
|
|
74
|
+
|
|
75
|
+
- name: Build sdist and wheel
|
|
76
|
+
run: uv build
|
|
77
|
+
|
|
78
|
+
# PyPI rejects a malformed long_description at upload time; catching it here is
|
|
79
|
+
# the one gap that skipping TestPyPI would otherwise leave.
|
|
80
|
+
- name: README must render on PyPI
|
|
81
|
+
run: uv run --with twine twine check dist/*
|
|
82
|
+
|
|
83
|
+
# A wheel that imports but cannot find its packaged commands.yaml is useless, and
|
|
84
|
+
# that failure only shows up outside the source tree.
|
|
85
|
+
- name: Smoke-test the built wheel in a clean environment
|
|
86
|
+
run: |
|
|
87
|
+
uv venv /tmp/verify
|
|
88
|
+
uv pip install --python /tmp/verify/bin/python dist/*.whl
|
|
89
|
+
/tmp/verify/bin/safereach --help
|
|
90
|
+
/tmp/verify/bin/safereach validate "journalctl -u nginx -n 5"
|
|
91
|
+
|
|
92
|
+
- uses: actions/upload-artifact@v4
|
|
93
|
+
with:
|
|
94
|
+
name: dist
|
|
95
|
+
path: dist/
|
|
96
|
+
|
|
97
|
+
publish-testpypi:
|
|
98
|
+
needs: build
|
|
99
|
+
if: inputs.target == 'testpypi'
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
environment:
|
|
102
|
+
name: testpypi
|
|
103
|
+
url: https://test.pypi.org/p/safereach
|
|
104
|
+
permissions:
|
|
105
|
+
id-token: write
|
|
106
|
+
steps:
|
|
107
|
+
- uses: actions/download-artifact@v4
|
|
108
|
+
with: { name: dist, path: dist }
|
|
109
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
110
|
+
with:
|
|
111
|
+
repository-url: https://test.pypi.org/legacy/
|
|
112
|
+
|
|
113
|
+
publish-pypi:
|
|
114
|
+
needs: build
|
|
115
|
+
if: inputs.target == 'pypi'
|
|
116
|
+
runs-on: ubuntu-latest
|
|
117
|
+
environment:
|
|
118
|
+
name: pypi
|
|
119
|
+
url: https://pypi.org/p/safereach
|
|
120
|
+
permissions:
|
|
121
|
+
id-token: write
|
|
122
|
+
steps:
|
|
123
|
+
- uses: actions/download-artifact@v4
|
|
124
|
+
with: { name: dist, path: dist }
|
|
125
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
safereach-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 imtiaz
|
|
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.
|