marsh-lib 0.3.0__tar.gz → 0.3.2__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.
- marsh_lib-0.3.2/.circleci/config.yml +125 -0
- marsh_lib-0.3.2/.github/workflows/docker-integration.yml +39 -0
- marsh_lib-0.3.2/.github/workflows/release-rehearsal.yml +179 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/.github/workflows/release.yml +8 -5
- marsh_lib-0.3.2/.github/workflows/ssh-integration.yml +59 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/.github/workflows/test.yml +25 -3
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/PKG-INFO +3 -1
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/README.md +2 -0
- marsh_lib-0.3.2/docs/api-migration.md +218 -0
- marsh_lib-0.3.2/docs/v0.3.2-test-portability.md +73 -0
- marsh_lib-0.3.2/docs/workflow-guide.md +347 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/pyproject.toml +1 -1
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/configuration.py +13 -10
- marsh_lib-0.3.2/src/marsh/core/dag_bridge.py +33 -0
- marsh_lib-0.3.2/src/marsh/core/diagnostics.py +15 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/runtime.py +29 -9
- marsh_lib-0.3.2/src/marsh/core/validation.py +61 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/docker/docker_command_grammar.py +1 -1
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/docker/docker_executor.py +10 -2
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/modifier_functions/case_conversion.py +1 -1
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/modifier_functions/readers.py +2 -2
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/processor_functions/printers.py +3 -2
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/processor_functions/redirections.py +1 -1
- marsh_lib-0.3.2/src/marsh/providers/__init__.py +5 -0
- marsh_lib-0.3.2/src/marsh/runtime/__init__.py +5 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/ssh/ssh_connector.py +1 -0
- marsh_lib-0.3.2/src/marsh/workflow/__init__.py +8 -0
- marsh_lib-0.3.0/.circleci/config.yml +0 -73
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/.github/ISSUE_TEMPLATE/other.md +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/.github/pull_request_template.md +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/.gitignore +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/LICENSE +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/Taskfile.yml +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/docs/P0_REPOSITORY_AUDIT.md +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/docs/P6_UX_COMPOSITION_EVALUATION.md +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/docs/architecture.md +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/run_act.sh +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/run_pytest.sh +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/run_tests_with_sysbox.sh +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/__init__.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/bash/__init__.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/bash/bash_factory.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/bash/bash_grammar.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/bash/bash_runner_decorators.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/bash/bash_script.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/constants.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/__init__.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/authenticator.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/cache.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/cmd_run_decorator.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/cmd_runner_spec.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/command_grammar.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/connector.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/conveyor.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/domain.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/executor.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/expression.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/observability.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/policies.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/providers.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/script.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/core/serialization.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/dag/__init__.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/dag/dag.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/dag/node.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/dag/startable.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/docker/__init__.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/exceptions.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/logger.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/modifier_functions/__init__.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/powershell/__init__.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/processor_functions/__init__.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/processor_functions/raisers.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/signals.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/ssh/__init__.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/ssh/ssh_command_grammar.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/ssh/ssh_factory.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/utils/__init__.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/src/marsh/utils/output_streams.py +0 -0
- {marsh_lib-0.3.0 → marsh_lib-0.3.2}/uv.lock +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
version: 2.1
|
|
2
|
+
|
|
3
|
+
jobs:
|
|
4
|
+
test:
|
|
5
|
+
parameters:
|
|
6
|
+
python-version:
|
|
7
|
+
type: string
|
|
8
|
+
docker:
|
|
9
|
+
- image: cimg/python:<< parameters.python-version >>
|
|
10
|
+
resource_class: medium
|
|
11
|
+
steps:
|
|
12
|
+
- checkout
|
|
13
|
+
- run:
|
|
14
|
+
name: Install uv
|
|
15
|
+
command: |
|
|
16
|
+
python -m pip install --upgrade pip
|
|
17
|
+
python -m pip install uv
|
|
18
|
+
- run:
|
|
19
|
+
name: Install dependencies
|
|
20
|
+
command: uv sync --all-groups
|
|
21
|
+
- run:
|
|
22
|
+
name: Run baseline tests
|
|
23
|
+
command: |
|
|
24
|
+
mkdir -p test-results
|
|
25
|
+
uv run pytest -vv --disable-warnings --tb=short \
|
|
26
|
+
--junitxml=test-results/junit.xml \
|
|
27
|
+
--ignore=tests/ssh/test_ssh_connector.py \
|
|
28
|
+
--ignore=tests/ssh/test_ssh_factory.py \
|
|
29
|
+
--ignore=tests/docker/
|
|
30
|
+
- store_test_results:
|
|
31
|
+
path: test-results
|
|
32
|
+
|
|
33
|
+
lint:
|
|
34
|
+
docker:
|
|
35
|
+
- image: cimg/python:3.10
|
|
36
|
+
resource_class: small
|
|
37
|
+
steps:
|
|
38
|
+
- checkout
|
|
39
|
+
- run:
|
|
40
|
+
name: Install uv
|
|
41
|
+
command: |
|
|
42
|
+
python -m pip install --upgrade pip
|
|
43
|
+
python -m pip install uv
|
|
44
|
+
- run:
|
|
45
|
+
name: Install dependencies
|
|
46
|
+
command: uv sync --all-groups
|
|
47
|
+
- run:
|
|
48
|
+
name: Run pflake8
|
|
49
|
+
command: uv run pflake8 src/marsh --select=E9,F --ignore=F401,F403,F405,F541,F811,F841
|
|
50
|
+
|
|
51
|
+
build:
|
|
52
|
+
docker:
|
|
53
|
+
- image: cimg/python:3.10
|
|
54
|
+
resource_class: small
|
|
55
|
+
steps:
|
|
56
|
+
- checkout
|
|
57
|
+
- run:
|
|
58
|
+
name: Install uv
|
|
59
|
+
command: |
|
|
60
|
+
python -m pip install --upgrade pip
|
|
61
|
+
python -m pip install uv
|
|
62
|
+
- run:
|
|
63
|
+
name: Install dependencies
|
|
64
|
+
command: uv sync --all-groups
|
|
65
|
+
- run:
|
|
66
|
+
name: Build wheel
|
|
67
|
+
command: uv build --wheel --out-dir dist -v
|
|
68
|
+
|
|
69
|
+
integration:
|
|
70
|
+
machine:
|
|
71
|
+
image: ubuntu-2404:current
|
|
72
|
+
docker_layer_caching: false
|
|
73
|
+
resource_class: medium
|
|
74
|
+
steps:
|
|
75
|
+
- checkout
|
|
76
|
+
- run:
|
|
77
|
+
name: Install uv
|
|
78
|
+
command: |
|
|
79
|
+
set -euxo pipefail
|
|
80
|
+
python3 -m pip install --upgrade pip
|
|
81
|
+
python3 -m pip install --user uv==0.12.12
|
|
82
|
+
USER_BIN="$(python3 -m site --user-base)/bin"
|
|
83
|
+
echo "export PATH=\"$USER_BIN:\$PATH\"" >> "$BASH_ENV"
|
|
84
|
+
export PATH="$USER_BIN:$PATH"
|
|
85
|
+
"$USER_BIN/uv" --version
|
|
86
|
+
command -v uv
|
|
87
|
+
- run:
|
|
88
|
+
name: Install dependencies
|
|
89
|
+
command: uv sync --all-groups
|
|
90
|
+
- run:
|
|
91
|
+
name: Validate dependency and platform contract
|
|
92
|
+
command: |
|
|
93
|
+
uv run python - \<<'PY'
|
|
94
|
+
import sys
|
|
95
|
+
from pathlib import Path
|
|
96
|
+
import tomllib
|
|
97
|
+
|
|
98
|
+
assert sys.version_info[:2] == (3, 12), sys.version
|
|
99
|
+
project = tomllib.loads(Path("pyproject.toml").read_text())["project"]
|
|
100
|
+
assert "optional-dependencies" not in project, "Unexpected optional dependency surface"
|
|
101
|
+
assert project["requires-python"] == ">=3.10,<3.13"
|
|
102
|
+
print("Python 3.12 integration environment validated; optional dependency surface is N/A.")
|
|
103
|
+
PY
|
|
104
|
+
- run:
|
|
105
|
+
name: Pull Docker integration test images
|
|
106
|
+
command: |
|
|
107
|
+
docker pull bash:latest
|
|
108
|
+
docker pull ubuntu:24.04
|
|
109
|
+
- run:
|
|
110
|
+
name: Run Docker integration tests
|
|
111
|
+
command: uv run pytest -vv --disable-warnings --tb=short tests/docker
|
|
112
|
+
- run:
|
|
113
|
+
name: Run SSH integration tests
|
|
114
|
+
command: uv run pytest -vv --disable-warnings --tb=short tests/ssh
|
|
115
|
+
|
|
116
|
+
workflows:
|
|
117
|
+
baseline:
|
|
118
|
+
jobs:
|
|
119
|
+
- test:
|
|
120
|
+
matrix:
|
|
121
|
+
parameters:
|
|
122
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
123
|
+
- lint
|
|
124
|
+
- build
|
|
125
|
+
- integration
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Docker Integration Smoke
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- develop
|
|
7
|
+
paths:
|
|
8
|
+
- "src/marsh/docker/**"
|
|
9
|
+
- "tests/docker/**"
|
|
10
|
+
- "pyproject.toml"
|
|
11
|
+
- "uv.lock"
|
|
12
|
+
- ".github/workflows/docker-integration.yml"
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
docker-integration:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
timeout-minutes: 10
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout
|
|
23
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
24
|
+
|
|
25
|
+
- name: Install uv
|
|
26
|
+
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
27
|
+
with:
|
|
28
|
+
version: "0.12.12"
|
|
29
|
+
enable-cache: false
|
|
30
|
+
|
|
31
|
+
- name: Install dependencies
|
|
32
|
+
run: uv sync --all-groups
|
|
33
|
+
|
|
34
|
+
- name: Pull integration image
|
|
35
|
+
run: docker pull bash:latest
|
|
36
|
+
|
|
37
|
+
- name: Run Docker smoke tests
|
|
38
|
+
run: |
|
|
39
|
+
uv run pytest -vv --disable-warnings --tb=short tests/docker/test_docker_container.py::test_docker_container_resource_lifecycle tests/docker/test_docker_command_executor.py::test_basic_command_without_piping
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
name: Release Rehearsal
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- chore/p7-release-rehearsal-and-matrix
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
rehearsal:
|
|
13
|
+
name: Build, publish, verify, and exercise recovery against TestPyPI
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
environment:
|
|
16
|
+
name: testpypi
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
id-token: write
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout exact rehearsal revision
|
|
22
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
|
+
with:
|
|
24
|
+
fetch-depth: 1
|
|
25
|
+
persist-credentials: false
|
|
26
|
+
|
|
27
|
+
- name: Install uv
|
|
28
|
+
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
29
|
+
with:
|
|
30
|
+
version: "0.12.12"
|
|
31
|
+
enable-cache: false
|
|
32
|
+
|
|
33
|
+
- name: Build wheel and source distribution once
|
|
34
|
+
run: uv build --no-sources --out-dir dist
|
|
35
|
+
|
|
36
|
+
- name: Validate exact rehearsal artifact set
|
|
37
|
+
env:
|
|
38
|
+
RELEASE_VERSION: "0.3.0"
|
|
39
|
+
run: |
|
|
40
|
+
python - <<'PY'
|
|
41
|
+
import os
|
|
42
|
+
from pathlib import Path
|
|
43
|
+
import tomllib
|
|
44
|
+
|
|
45
|
+
version = os.environ["RELEASE_VERSION"]
|
|
46
|
+
project = tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"]
|
|
47
|
+
if project != version:
|
|
48
|
+
raise SystemExit(f"Package version {project!r} does not match rehearsal version {version!r}")
|
|
49
|
+
|
|
50
|
+
files = sorted(
|
|
51
|
+
p.name for p in Path("dist").iterdir()
|
|
52
|
+
if p.is_file() and not p.name.startswith(".")
|
|
53
|
+
)
|
|
54
|
+
wheels = [name for name in files if name.endswith(".whl")]
|
|
55
|
+
sdists = [name for name in files if name.endswith(".tar.gz")]
|
|
56
|
+
if len(wheels) != 1 or len(sdists) != 1 or len(files) != 2:
|
|
57
|
+
raise SystemExit(f"Expected exactly one wheel and one sdist, found: {files}")
|
|
58
|
+
|
|
59
|
+
if not wheels[0].startswith(f"marsh_lib-{version}-"):
|
|
60
|
+
raise SystemExit(f"Unexpected wheel filename: {wheels[0]}")
|
|
61
|
+
if sdists[0] != f"marsh_lib-{version}.tar.gz":
|
|
62
|
+
raise SystemExit(f"Unexpected sdist filename: {sdists[0]}")
|
|
63
|
+
print("\n".join(files))
|
|
64
|
+
PY
|
|
65
|
+
|
|
66
|
+
- name: Smoke test wheel
|
|
67
|
+
run: uv run --isolated --no-project --with dist/*.whl python -c "import marsh; print(marsh.__file__)"
|
|
68
|
+
|
|
69
|
+
- name: Smoke test source distribution
|
|
70
|
+
run: uv run --isolated --no-project --with dist/*.tar.gz python -c "import marsh; print(marsh.__file__)"
|
|
71
|
+
|
|
72
|
+
- name: Record exact distribution hashes
|
|
73
|
+
run: |
|
|
74
|
+
sha256sum dist/*.whl dist/*.tar.gz | tee dist/SHA256SUMS
|
|
75
|
+
|
|
76
|
+
- name: Exercise verifier failure and recovery path
|
|
77
|
+
run: |
|
|
78
|
+
set -euo pipefail
|
|
79
|
+
mkdir -p recovery-test
|
|
80
|
+
cp dist/*.whl recovery-test/
|
|
81
|
+
python - <<'PY'
|
|
82
|
+
from pathlib import Path
|
|
83
|
+
wheel = next(Path("recovery-test").glob("*.whl"))
|
|
84
|
+
wheel.write_bytes(wheel.read_bytes() + b"intentional-recovery-test")
|
|
85
|
+
PY
|
|
86
|
+
|
|
87
|
+
set +e
|
|
88
|
+
python - <<'PY'
|
|
89
|
+
import hashlib
|
|
90
|
+
from pathlib import Path
|
|
91
|
+
|
|
92
|
+
expected = {
|
|
93
|
+
p.name: hashlib.sha256(p.read_bytes()).hexdigest()
|
|
94
|
+
for p in Path("dist").glob("*.whl")
|
|
95
|
+
}
|
|
96
|
+
tampered = next(Path("recovery-test").glob("*.whl"))
|
|
97
|
+
if hashlib.sha256(tampered.read_bytes()).hexdigest() != expected[tampered.name]:
|
|
98
|
+
raise SystemExit(1)
|
|
99
|
+
PY
|
|
100
|
+
verifier_status=$?
|
|
101
|
+
set -e
|
|
102
|
+
|
|
103
|
+
if [ "$verifier_status" -eq 0 ]; then
|
|
104
|
+
echo "ERROR: tampered artifact was not detected"
|
|
105
|
+
exit 1
|
|
106
|
+
fi
|
|
107
|
+
echo "Recovery drill: tampered artifact correctly rejected"
|
|
108
|
+
|
|
109
|
+
rm -rf recovery-test
|
|
110
|
+
python - <<'PY'
|
|
111
|
+
import hashlib
|
|
112
|
+
from pathlib import Path
|
|
113
|
+
|
|
114
|
+
for p in list(Path("dist").glob("*.whl")) + list(Path("dist").glob("*.tar.gz")):
|
|
115
|
+
recorded = next(
|
|
116
|
+
line.split()[0]
|
|
117
|
+
for line in Path("dist/SHA256SUMS").read_text().splitlines()
|
|
118
|
+
if Path(line.split(maxsplit=1)[1]).name == p.name
|
|
119
|
+
)
|
|
120
|
+
actual = hashlib.sha256(p.read_bytes()).hexdigest()
|
|
121
|
+
assert recorded == actual, p.name
|
|
122
|
+
print("Recovery drill: original immutable artifacts revalidated successfully")
|
|
123
|
+
PY
|
|
124
|
+
|
|
125
|
+
- name: Publish exact artifacts to TestPyPI
|
|
126
|
+
run: uv publish --publish-url https://test.pypi.org/legacy/
|
|
127
|
+
|
|
128
|
+
- name: Verify TestPyPI hashes
|
|
129
|
+
env:
|
|
130
|
+
RELEASE_VERSION: "0.3.0"
|
|
131
|
+
run: |
|
|
132
|
+
python - <<'PY'
|
|
133
|
+
import hashlib
|
|
134
|
+
import json
|
|
135
|
+
import os
|
|
136
|
+
from pathlib import Path
|
|
137
|
+
import urllib.request
|
|
138
|
+
|
|
139
|
+
version = os.environ["RELEASE_VERSION"]
|
|
140
|
+
url = f"https://test.pypi.org/pypi/marsh-lib/{version}/json"
|
|
141
|
+
with urllib.request.urlopen(url, timeout=30) as response:
|
|
142
|
+
payload = json.load(response)
|
|
143
|
+
|
|
144
|
+
expected = {
|
|
145
|
+
p.name: hashlib.sha256(p.read_bytes()).hexdigest()
|
|
146
|
+
for pattern in ("*.whl", "*.tar.gz")
|
|
147
|
+
for p in Path("dist").glob(pattern)
|
|
148
|
+
if p.is_file()
|
|
149
|
+
}
|
|
150
|
+
published = {
|
|
151
|
+
item["filename"]: item["digests"]["sha256"]
|
|
152
|
+
for item in payload["urls"]
|
|
153
|
+
}
|
|
154
|
+
missing = sorted(set(expected) - set(published))
|
|
155
|
+
unexpected = sorted(set(published) - set(expected))
|
|
156
|
+
mismatched = sorted(
|
|
157
|
+
name for name, digest in expected.items()
|
|
158
|
+
if published.get(name) != digest
|
|
159
|
+
)
|
|
160
|
+
if missing or unexpected or mismatched:
|
|
161
|
+
raise SystemExit(
|
|
162
|
+
"TestPyPI verification failed; "
|
|
163
|
+
f"missing={missing}, unexpected={unexpected}, mismatched={mismatched}"
|
|
164
|
+
)
|
|
165
|
+
print(f"Verified {len(expected)} TestPyPI distribution hashes for marsh-lib {version}")
|
|
166
|
+
PY
|
|
167
|
+
|
|
168
|
+
- name: Install rehearsal package from TestPyPI
|
|
169
|
+
run: |
|
|
170
|
+
uv run --isolated --no-project --with "marsh-lib==0.3.0" \
|
|
171
|
+
--index-url https://test.pypi.org/simple/ \
|
|
172
|
+
--extra-index-url https://pypi.org/simple/ \
|
|
173
|
+
python -c "import marsh; print(marsh.__file__)"
|
|
174
|
+
|
|
175
|
+
- name: Rehearsal summary
|
|
176
|
+
run: |
|
|
177
|
+
echo "Non-production release rehearsal completed."
|
|
178
|
+
echo "Production PyPI was not contacted."
|
|
179
|
+
echo "The exact artifact set was built once, published to TestPyPI, hash-verified, installed, and recovery verification was exercised."
|
|
@@ -193,8 +193,9 @@ jobs:
|
|
|
193
193
|
|
|
194
194
|
expected = {
|
|
195
195
|
path.name: hashlib.sha256(path.read_bytes()).hexdigest()
|
|
196
|
-
for
|
|
197
|
-
|
|
196
|
+
for pattern in ("*.whl", "*.tar.gz")
|
|
197
|
+
for path in Path("dist").glob(pattern)
|
|
198
|
+
if path.is_file()
|
|
198
199
|
}
|
|
199
200
|
published = {
|
|
200
201
|
item["filename"]: item["digests"]["sha256"]
|
|
@@ -202,13 +203,15 @@ jobs:
|
|
|
202
203
|
}
|
|
203
204
|
|
|
204
205
|
missing = sorted(set(expected) - set(published))
|
|
206
|
+
unexpected = sorted(set(published) - set(expected))
|
|
205
207
|
mismatched = sorted(
|
|
206
208
|
name for name, digest in expected.items()
|
|
207
209
|
if published.get(name) != digest
|
|
208
210
|
)
|
|
209
|
-
if missing or mismatched:
|
|
211
|
+
if missing or unexpected or mismatched:
|
|
210
212
|
raise SystemExit(
|
|
211
|
-
|
|
213
|
+
"PyPI verification failed; "
|
|
214
|
+
f"missing={missing}, unexpected={unexpected}, mismatched={mismatched}"
|
|
212
215
|
)
|
|
213
|
-
print(f"Verified {len(expected)} published
|
|
216
|
+
print(f"Verified {len(expected)} published distribution hashes for marsh-lib {version}")
|
|
214
217
|
PY
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: SSH Integration Smoke
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- develop
|
|
7
|
+
paths:
|
|
8
|
+
- "src/marsh/ssh/**"
|
|
9
|
+
- "tests/ssh/**"
|
|
10
|
+
- "pyproject.toml"
|
|
11
|
+
- "uv.lock"
|
|
12
|
+
- ".github/workflows/ssh-integration.yml"
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
ssh-integration:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
timeout-minutes: 10
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout
|
|
23
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
24
|
+
|
|
25
|
+
- name: Install uv
|
|
26
|
+
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
27
|
+
with:
|
|
28
|
+
version: "0.12.12"
|
|
29
|
+
enable-cache: false
|
|
30
|
+
|
|
31
|
+
- name: Install dependencies
|
|
32
|
+
run: uv sync --all-groups
|
|
33
|
+
|
|
34
|
+
- name: Configure local SSH server
|
|
35
|
+
run: |
|
|
36
|
+
sudo apt-get update -qq
|
|
37
|
+
sudo apt-get install -y openssh-server
|
|
38
|
+
sudo useradd --create-home --shell /bin/bash marsh-ci
|
|
39
|
+
sudo install -d -m 700 -o marsh-ci -g marsh-ci /home/marsh-ci/.ssh
|
|
40
|
+
echo "marsh-ci:marsh-password" | sudo chpasswd
|
|
41
|
+
sudo mkdir -p /run/sshd
|
|
42
|
+
cat > "$RUNNER_TEMP/sshd_config" <<EOF
|
|
43
|
+
Port 2222
|
|
44
|
+
ListenAddress 127.0.0.1
|
|
45
|
+
HostKey /etc/ssh/ssh_host_ed25519_key
|
|
46
|
+
PubkeyAuthentication yes
|
|
47
|
+
PasswordAuthentication yes
|
|
48
|
+
PermitRootLogin no
|
|
49
|
+
UsePAM no
|
|
50
|
+
PidFile $RUNNER_TEMP/sshd.pid
|
|
51
|
+
AllowUsers marsh-ci
|
|
52
|
+
EOF
|
|
53
|
+
sudo /usr/sbin/sshd -f "$RUNNER_TEMP/sshd_config"
|
|
54
|
+
|
|
55
|
+
- name: Run SSH smoke test
|
|
56
|
+
env:
|
|
57
|
+
MARSH_SSH_TARGET: marsh-ci@127.0.0.1:2222
|
|
58
|
+
MARSH_SSH_PASSWORD: marsh-password
|
|
59
|
+
run: uv run pytest -s -vv --disable-warnings --tb=short tests/ssh/test_local_sshd_smoke.py
|
|
@@ -4,7 +4,7 @@ on:
|
|
|
4
4
|
push:
|
|
5
5
|
branches: ["*"]
|
|
6
6
|
pull_request:
|
|
7
|
-
branches: ["main"]
|
|
7
|
+
branches: ["develop", "main"]
|
|
8
8
|
workflow_dispatch:
|
|
9
9
|
|
|
10
10
|
permissions:
|
|
@@ -16,7 +16,7 @@ jobs:
|
|
|
16
16
|
runs-on: ubuntu-latest
|
|
17
17
|
steps:
|
|
18
18
|
- name: Checkout code
|
|
19
|
-
uses: actions/checkout@
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
20
|
|
|
21
21
|
- name: Install uv
|
|
22
22
|
uses: astral-sh/setup-uv@v10.2.0
|
|
@@ -27,7 +27,6 @@ jobs:
|
|
|
27
27
|
run: |
|
|
28
28
|
uv sync --all-groups
|
|
29
29
|
|
|
30
|
-
# TODO: Setup Docker for testing docker components (and possible ssh components).
|
|
31
30
|
- name: Run pytest
|
|
32
31
|
run: |
|
|
33
32
|
cd src
|
|
@@ -45,3 +44,26 @@ jobs:
|
|
|
45
44
|
with:
|
|
46
45
|
coverageFile: coverage.xml
|
|
47
46
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
47
|
+
|
|
48
|
+
windows_portability:
|
|
49
|
+
name: Windows portability (Python 3.12)
|
|
50
|
+
runs-on: windows-latest
|
|
51
|
+
steps:
|
|
52
|
+
- name: Checkout code
|
|
53
|
+
uses: actions/checkout@v4
|
|
54
|
+
|
|
55
|
+
- name: Install uv
|
|
56
|
+
uses: astral-sh/setup-uv@v10.2.0
|
|
57
|
+
with:
|
|
58
|
+
python-version: '3.12'
|
|
59
|
+
|
|
60
|
+
- name: Install dependencies
|
|
61
|
+
run: uv sync --all-groups
|
|
62
|
+
|
|
63
|
+
- name: Run portable pytest suite
|
|
64
|
+
run: >
|
|
65
|
+
uv run pytest -vv --disable-warnings --tb=short
|
|
66
|
+
--ignore=tests/bash
|
|
67
|
+
--ignore=tests/docker
|
|
68
|
+
--ignore=tests/ssh
|
|
69
|
+
tests/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: marsh-lib
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Lightweight, extensible Python library for building, managing, and executing command workflows.
|
|
5
5
|
Project-URL: Repository, https://github.com/CedricAnover/marsh
|
|
6
6
|
Author-email: Cedric Anover <cedric.anover@hotmail.com>
|
|
@@ -609,6 +609,8 @@ remain important compatibility surfaces.
|
|
|
609
609
|
|
|
610
610
|
New functionality should prefer the canonical workflow contracts and adapt existing mechanisms into them rather than introducing a separate execution model.
|
|
611
611
|
|
|
612
|
+
For migration examples and legacy/deprecation guidance, see [`docs/api-migration.md`](docs/api-migration.md).
|
|
613
|
+
|
|
612
614
|
## Contributing
|
|
613
615
|
|
|
614
616
|
Before making architectural or public-interface changes:
|
|
@@ -582,6 +582,8 @@ remain important compatibility surfaces.
|
|
|
582
582
|
|
|
583
583
|
New functionality should prefer the canonical workflow contracts and adapt existing mechanisms into them rather than introducing a separate execution model.
|
|
584
584
|
|
|
585
|
+
For migration examples and legacy/deprecation guidance, see [`docs/api-migration.md`](docs/api-migration.md).
|
|
586
|
+
|
|
585
587
|
## Contributing
|
|
586
588
|
|
|
587
589
|
Before making architectural or public-interface changes:
|