replico 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.
Files changed (61) hide show
  1. replico-0.1.0/.freebuff/project-id +1 -0
  2. replico-0.1.0/.github/workflows/ci.yml +76 -0
  3. replico-0.1.0/.gitignore +36 -0
  4. replico-0.1.0/CHANGELOG.md +75 -0
  5. replico-0.1.0/LICENSE +21 -0
  6. replico-0.1.0/PKG-INFO +447 -0
  7. replico-0.1.0/README.md +392 -0
  8. replico-0.1.0/SECURITY.md +103 -0
  9. replico-0.1.0/pyproject.toml +77 -0
  10. replico-0.1.0/src/replico/__init__.py +7 -0
  11. replico-0.1.0/src/replico/__main__.py +6 -0
  12. replico-0.1.0/src/replico/analysis/__init__.py +1 -0
  13. replico-0.1.0/src/replico/analysis/classifier.py +304 -0
  14. replico-0.1.0/src/replico/analysis/logs.py +216 -0
  15. replico-0.1.0/src/replico/cli.py +339 -0
  16. replico-0.1.0/src/replico/cmds.py +298 -0
  17. replico-0.1.0/src/replico/config.py +189 -0
  18. replico-0.1.0/src/replico/environments/__init__.py +25 -0
  19. replico-0.1.0/src/replico/environments/base.py +64 -0
  20. replico-0.1.0/src/replico/environments/fingerprint.py +222 -0
  21. replico-0.1.0/src/replico/environments/node.py +35 -0
  22. replico-0.1.0/src/replico/environments/python.py +289 -0
  23. replico-0.1.0/src/replico/errors.py +80 -0
  24. replico-0.1.0/src/replico/execution/__init__.py +1 -0
  25. replico-0.1.0/src/replico/execution/docker.py +166 -0
  26. replico-0.1.0/src/replico/execution/runner.py +212 -0
  27. replico-0.1.0/src/replico/flows.py +840 -0
  28. replico-0.1.0/src/replico/github/__init__.py +1 -0
  29. replico-0.1.0/src/replico/github/client.py +258 -0
  30. replico-0.1.0/src/replico/github/refs.py +76 -0
  31. replico-0.1.0/src/replico/gitrepo.py +179 -0
  32. replico-0.1.0/src/replico/models.py +157 -0
  33. replico-0.1.0/src/replico/pipeline.py +740 -0
  34. replico-0.1.0/src/replico/security/__init__.py +34 -0
  35. replico-0.1.0/src/replico/security/guard.py +122 -0
  36. replico-0.1.0/src/replico/security/redaction.py +270 -0
  37. replico-0.1.0/src/replico/storage/__init__.py +1 -0
  38. replico-0.1.0/src/replico/storage/store.py +201 -0
  39. replico-0.1.0/src/replico/ui.py +146 -0
  40. replico-0.1.0/src/replico/util.py +136 -0
  41. replico-0.1.0/src/replico/workflow/__init__.py +1 -0
  42. replico-0.1.0/src/replico/workflow/detector.py +271 -0
  43. replico-0.1.0/src/replico/workflow/matcher.py +101 -0
  44. replico-0.1.0/src/replico/workflow/parser.py +363 -0
  45. replico-0.1.0/tests/conftest.py +253 -0
  46. replico-0.1.0/tests/test_classifier.py +78 -0
  47. replico-0.1.0/tests/test_cli.py +101 -0
  48. replico-0.1.0/tests/test_config.py +88 -0
  49. replico-0.1.0/tests/test_exit_codes.py +53 -0
  50. replico-0.1.0/tests/test_fingerprint.py +98 -0
  51. replico-0.1.0/tests/test_github_client.py +215 -0
  52. replico-0.1.0/tests/test_gitrepo.py +56 -0
  53. replico-0.1.0/tests/test_integration_reproduce.py +248 -0
  54. replico-0.1.0/tests/test_logs.py +77 -0
  55. replico-0.1.0/tests/test_refs.py +58 -0
  56. replico-0.1.0/tests/test_runner.py +77 -0
  57. replico-0.1.0/tests/test_security_guard.py +81 -0
  58. replico-0.1.0/tests/test_security_redact.py +136 -0
  59. replico-0.1.0/tests/test_store.py +116 -0
  60. replico-0.1.0/tests/test_workflow_detector.py +168 -0
  61. replico-0.1.0/tests/test_workflow_parser.py +142 -0
@@ -0,0 +1 @@
1
+ 49c8c90f-46e1-4c45-a454-0861ed60b128
@@ -0,0 +1,76 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ name: test (${{ matrix.os }}, py${{ matrix.python }})
14
+ runs-on: ${{ matrix.os }}
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ os: [ubuntu-latest, windows-latest, macos-latest]
19
+ python: ["3.11", "3.12", "3.13", "3.14"]
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python }}
25
+ cache: pip
26
+ - name: Install
27
+ run: python -m pip install --upgrade pip && python -m pip install -e ".[dev]"
28
+ - name: Test (offline suite, mocked GitHub)
29
+ run: python -m pytest -q
30
+ env:
31
+ # Never reach out to the network during tests.
32
+ NO_PROXY: "*"
33
+ no_proxy: "*"
34
+
35
+ quality:
36
+ name: lint + types
37
+ runs-on: ubuntu-latest
38
+ strategy:
39
+ matrix:
40
+ python: ["3.11", "3.14"]
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+ - uses: actions/setup-python@v5
44
+ with:
45
+ python-version: ${{ matrix.python }}
46
+ - name: Install
47
+ run: python -m pip install -e ".[dev]"
48
+ - name: Lint
49
+ run: ruff check src tests
50
+ - name: Format
51
+ run: ruff format --check src tests
52
+ - name: Type checks
53
+ run: mypy src/replico
54
+
55
+ package:
56
+ name: build + package validation
57
+ runs-on: ubuntu-latest
58
+ steps:
59
+ - uses: actions/checkout@v4
60
+ - uses: actions/setup-python@v5
61
+ with:
62
+ python-version: "3.13"
63
+ - name: Install build tooling
64
+ run: python -m pip install --upgrade pip build
65
+ - name: Build distributions
66
+ run: python -m build
67
+ - name: Validate wheel metadata
68
+ run: python -m pip install --force-reinstall dist/*.whl
69
+ - name: Smoke-test installed CLI
70
+ run: replico version && replico help >/dev/null
71
+ - name: Upload artifacts
72
+ uses: actions/upload-artifact@v4
73
+ with:
74
+ name: replico-dist
75
+ path: dist/
76
+ if-no-files-found: error
@@ -0,0 +1,36 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Virtual environments
7
+ .venv/
8
+ venv/
9
+ env/
10
+
11
+ # Replico's own artifacts (when dogfooding against this repository)
12
+ .replico/
13
+
14
+ # Test / coverage artifacts
15
+ .pytest_cache/
16
+ .coverage
17
+ coverage.xml
18
+ htmlcov/
19
+ .mypy_cache/
20
+ .ruff_cache/
21
+
22
+ # Build artifacts
23
+ build/
24
+ dist/
25
+ *.egg-info/
26
+ *.egg
27
+
28
+ # OS / editor noise
29
+ .DS_Store
30
+ Thumbs.db
31
+ *.swp
32
+ .idea/
33
+ .vscode/
34
+
35
+ # Scratch / probe files
36
+ _probe_*.py
@@ -0,0 +1,75 @@
1
+ # Changelog
2
+
3
+ All notable changes to Replico are documented here. Format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and semantic
5
+ versioning (see [pyproject.toml](pyproject.toml)).
6
+
7
+ ## [0.1.0] - 2026-09-04
8
+
9
+ First public release: GitHub Actions failures → local reproduction for
10
+ Python workflows.
11
+
12
+ ### Added
13
+
14
+ - **Core command** — `replico <run-url>` (and `replico reproduce …`,
15
+ `replico run <run-id>`) walks a failed GitHub Actions run end to end:
16
+ run identification → failed job → failed step → workflow parsing →
17
+ environment/dependency detection → reproduction plan → safe local
18
+ execution → honest verdict.
19
+ - **GitHub integration** — URL/ref parsing for `actions/runs/<id>` links,
20
+ `GITHUB_TOKEN` and `gh` CLI auth, public-repo anonymous access, run/job/
21
+ step listing, log download, workflow YAML retrieval by commit SHA.
22
+ - **Workflow parser** — hardened PyYAML-based parser (memoized alias
23
+ expansion, node budgets, depth caps against billion-laughs/recursion
24
+ attacks) supporting `runs-on`, `steps`, `uses`, `run`, `with`, `env`,
25
+ `defaults`, `working-directory`, `shell`, `strategy.matrix`, `if`.
26
+ - **Job/step selection** — automatic failed-job detection with interactive
27
+ picker when several fail; `--job` to select explicitly; failed-step
28
+ identification from job conclusion + annotations.
29
+ - **Ecosystem adapters** — pluggable `EcosystemAdapter` protocol; v0.1
30
+ ships Python (setup-action versions, requirements/pyproject/setup files,
31
+ `pip install`/`pytest`/`unittest` detection). Node adapter registers but
32
+ reports unsupported until v0.3.
33
+ - **Local execution** — explicit-interpreter script runner (no `shell=True`),
34
+ per-shell availability probing, risk audit of every workflow command
35
+ (elevation/destructive/network-exec/suspicious patterns) with mandatory
36
+ confirmation, optional `--docker` isolation with automatic fallback.
37
+ - **Failure analysis** — log compression to relevant evidence lines,
38
+ evidence-based classification (test/build/dependency/version/missing
39
+ tool/file/env/timeout/network/permission/config/unknown), confidence
40
+ scoring, GitHub annotation anchors.
41
+ - **Verdicts** — `reproduced`, `partially_reproduced`, `not_reproduced`,
42
+ `unsupported`; environment parity estimate comparing local machine to the
43
+ CI runner; never over-claims reproduction.
44
+ - **SecretShield integration** — `Sanitizer` adapter over the public
45
+ SecretShield API (`redact`/`detect`/`configure`) plus a compatibility
46
+ layer for known low-entropy literal secrets; every UI line, JSON payload
47
+ and `.replico/` artifact passes through it. Debug/verbose modes respect
48
+ redaction; stream-level protection via `enable()` where appropriate.
49
+ - **`.replico/` store** — redacted `reproduction.json`, `environment.json`,
50
+ `workflow.yml`, `commands.txt`, `differences.json`, `README.md`;
51
+ `rerun`, `status`, `diff`, `env`, `clean`, `capture` commands.
52
+ - **CLI polish** — rich formatting with `--plain`, machine-readable `--json`,
53
+ stable exit codes (0–6, 70), progress lines, sensible colors.
54
+ - **Offline test suite** — mocked GitHub API (`FakeGitHub`), fixture
55
+ repositories created on the fly, security tests for leakage, injection,
56
+ traversal, malicious YAML/filenames/env.
57
+ - **Packaging/docs** — modern `pyproject.toml` (hatchling), console entry
58
+ point, README, SECURITY model, own GitHub Actions CI
59
+ (3 OS × Python 3.11–3.14 + lint/types/build validation).
60
+
61
+ ### Security
62
+
63
+ - New dependency: `secretshield>=0.4.2` (see `replico/security/redaction.py`).
64
+ - Workflow commands are audited before execution; nothing elevated or
65
+ destructive runs without explicit `--yes`-free confirmation.
66
+ - Repository/job names are validated before touching URLs, paths or
67
+ subprocess arguments; `safe_join` blocks path traversal.
68
+
69
+ ### Notes / limitations
70
+
71
+ - v0.1 targets Python workflows on public repositories. Node/Go/Rust and
72
+ Docker-first flows are recognized but reported as unsupported rather than
73
+ half-reproduced.
74
+ - The environment parity figure is a transparent heuristic over a handful of
75
+ weighted checks — an estimate, never a guarantee.
replico-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sam3360
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.
replico-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,447 @@
1
+ Metadata-Version: 2.5
2
+ Name: replico
3
+ Version: 0.1.0
4
+ Summary: Turn GitHub Actions CI failures into locally reproducible failures.
5
+ Author: Samarth Chugh (Sam3360)
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Sam3360
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ License-File: LICENSE
28
+ Keywords: ci,developer-tools,github-actions,reproduction,testing
29
+ Classifier: Development Status :: 3 - Alpha
30
+ Classifier: Environment :: Console
31
+ Classifier: Intended Audience :: Developers
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Operating System :: OS Independent
34
+ Classifier: Programming Language :: Python :: 3
35
+ Classifier: Programming Language :: Python :: 3.11
36
+ Classifier: Programming Language :: Python :: 3.12
37
+ Classifier: Programming Language :: Python :: 3.13
38
+ Classifier: Programming Language :: Python :: 3.14
39
+ Classifier: Topic :: Software Development :: Build Tools
40
+ Classifier: Topic :: Software Development :: Testing
41
+ Requires-Python: >=3.11
42
+ Requires-Dist: pyyaml>=6.0
43
+ Requires-Dist: requests>=2.28
44
+ Requires-Dist: rich>=13.7
45
+ Requires-Dist: secretshield>=0.4.2
46
+ Provides-Extra: dev
47
+ Requires-Dist: build>=1.2; extra == 'dev'
48
+ Requires-Dist: mypy>=1.11; extra == 'dev'
49
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
50
+ Requires-Dist: pytest>=8.0; extra == 'dev'
51
+ Requires-Dist: ruff>=0.6; extra == 'dev'
52
+ Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
53
+ Requires-Dist: types-requests>=2.31; extra == 'dev'
54
+ Description-Content-Type: text/markdown
55
+
56
+ # Replico
57
+
58
+ **CI failures, reproduced locally.**
59
+
60
+ GitHub Actions fails → `replico <run-url>` → the failing command runs on your
61
+ machine → you get a **reproducible failure** with evidence.
62
+
63
+ Replico is a local-first developer tool. It reads a failed GitHub Actions run,
64
+ figures out *which job failed, which step failed and why*, reconstructs the
65
+ relevant environment (Python version, dependencies, environment variables it
66
+ can), and replays the failing step on your machine. It then tells you — with
67
+ honest confidence levels — whether the CI failure was reproduced.
68
+
69
+ > **The core promise:** turn CI failures into locally reproducible failures
70
+ > whenever Replico can reconstruct the relevant conditions. Replico never
71
+ > claims to be a perfect clone of a GitHub-hosted runner, and never claims a
72
+ > reproduction it cannot back with evidence.
73
+
74
+ ---
75
+
76
+ ## The problem
77
+
78
+ ```text
79
+ Developer pushes code → CI fails → Developer reads logs
80
+ → Guesses what went wrong → Changes code → Pushes again → CI fails again
81
+ ```
82
+
83
+ ## How it works
84
+
85
+ ```text
86
+ GitHub Actions fails
87
+
88
+ replico <run-url> # or: replico run <run-id>
89
+
90
+ Analyze the failed workflow
91
+
92
+ Reconstruct the relevant environment
93
+
94
+ Run the failing command locally
95
+
96
+ REPRODUCED ✓ / NOT REPRODUCED ✗ / PARTIAL ⚠ (with evidence and parity)
97
+
98
+ .replico/ saved — iterate: fix code → replico rerun
99
+ ```
100
+
101
+ ## Quick start
102
+
103
+ ```bash
104
+ pip install replico
105
+
106
+ # from the checkout of the repository whose CI failed:
107
+ replico https://github.com/example/project/actions/runs/123456789
108
+ ```
109
+
110
+ That's it. No account, no API key, no cloud service. Public repositories work
111
+ without any token. Private repositories need either `GITHUB_TOKEN` in your
112
+ environment or the GitHub CLI (`gh auth login`).
113
+
114
+ GitHub's API only serves **job log downloads to authenticated requests**, even
115
+ for public repositories. Without a token Replico still finds the failed run,
116
+ job and step, fetches the workflow YAML at the exact commit, and reproduces
117
+ from the workflow — but the log-level failure analysis needs a token:
118
+
119
+ ```bash
120
+ export GITHUB_TOKEN=ghp_... # or: gh auth login
121
+ ```
122
+
123
+ Example output (abridged):
124
+
125
+ ```text
126
+ ─────────────────────────────────────────────────────────────
127
+ ✓ failed job: Python 3.13
128
+ ✓ failed step: Run tests
129
+
130
+ REPLICO REPRODUCTION PLAN
131
+
132
+ Repository: example/project
133
+ Commit: a82f91c
134
+ Workflow: Tests
135
+ Job: Python 3.13
136
+ Failed step: Run tests
137
+ Runner: ubuntu-24.04
138
+ Ecosystem: python
139
+
140
+ Detected setup:
141
+ ✓ checkout
142
+ ✓ Python 3.13
143
+ • pip install -r requirements.txt
144
+
145
+ Reproducing...
146
+ ✓ environment ready
147
+ ✓ dependencies installed
148
+ ✗ the failing command FAILED locally (exit 1, 2.4 s)
149
+
150
+ REPLICO RESULT — CI FAILURE REPRODUCED
151
+ • the same failing test(s) reproduced locally: tests/test_auth.py::test_login
152
+ ```
153
+
154
+ After a code change:
155
+
156
+ ```bash
157
+ replico rerun
158
+ ```
159
+
160
+ ```text
161
+ REPLICO RERUN
162
+ ✓ environment ready
163
+ ✓ running the previously failing step…
164
+
165
+ ✓ the failing command PASSED locally
166
+
167
+ REPLICO RESULT — CI FAILURE NOT REPRODUCED
168
+ • the previously reproduced failure no longer occurs locally.
169
+ ```
170
+
171
+ (Wording is careful on purpose: a local pass does *not* prove CI will pass.)
172
+
173
+ ## Installation
174
+
175
+ Requirements: **Python 3.11+**, `git`. Docker is optional but recommended
176
+ when the CI runner OS differs from your machine.
177
+
178
+ ```bash
179
+ pip install replico # installs the `replico` command
180
+ # or from source:
181
+ pip install -e ".[dev]" # development install
182
+ ```
183
+
184
+ SecretShield (`secretshield>=0.4.2`) is a real dependency: every log line,
185
+ environment value, command output and artifact that Replico displays or saves
186
+ passes through SecretShield's detection/redaction (see [Security](#security)).
187
+
188
+ ## Usage
189
+
190
+ ```bash
191
+ # Reproduce a failed run (the flagship command — same as `replico reproduce …`)
192
+ replico https://github.com/owner/repo/actions/runs/123456789
193
+ replico reproduce https://github.com/owner/repo/actions/runs/123456789
194
+ replico run 123456789 # run id; repository read from git origin
195
+
196
+ # Iterate after code changes
197
+ replico rerun # re-run the saved reproduction
198
+ replico status # saved state vs current checkout
199
+ replico diff # what changed since the CI failure
200
+
201
+ # Inspect
202
+ replico env # sanitized local environment fingerprint
203
+ replico config # effective configuration (no secrets)
204
+ replico version
205
+
206
+ # Hygiene
207
+ replico clean # remove .replico/ (confirmed)
208
+
209
+ # Inside CI (e.g. under `if: failure()`), capture context for later:
210
+ replico capture
211
+ ```
212
+
213
+ Common flags:
214
+
215
+ | Flag | Meaning |
216
+ | --- | --- |
217
+ | `--job <job>` | which failed job to reproduce (multi-job runs) |
218
+ | `--step <name>` | which step to reproduce (default: the failing step) |
219
+ | `--docker` / `--no-docker` | force / forbid Docker isolation |
220
+ | `--offline` | use only locally saved data — no GitHub requests |
221
+ | `--json` | machine-readable JSON on stdout (all prose goes to stderr) |
222
+ | `--plain` | no colors, no decorations (CI/log capture) |
223
+ | `--yes` | accept confirmations non-interactively |
224
+ | `--verbose` / `--debug` | more detail (still redacted) |
225
+ | `--clean` | remove an existing `.replico/` before reproducing |
226
+
227
+ ### Multiple failed jobs
228
+
229
+ ```text
230
+ 2 failed jobs found.
231
+
232
+ 1. test-python
233
+ 2. integration-linux
234
+
235
+ Use --job to select one:
236
+ replico <run-url> --job test-python
237
+ ```
238
+
239
+ When exactly one job failed it is selected automatically. The same logic
240
+ applies inside the job: the first failed step is chosen.
241
+
242
+ ### Matrix workflows
243
+
244
+ Matrix combinations are matched back to the workflow YAML (best effort:
245
+ job id, explicit `name:`, and matrix-expanded display names). The matching
246
+ combination's variables are used when rendering the steps.
247
+
248
+ ## Supported workflows (v0.1)
249
+
250
+ | Ecosystem | Status |
251
+ | --- | --- |
252
+ | Python (`actions/setup-python`, `pip`, `pytest`, `python -m unittest`, …) | **supported** |
253
+ | Plain shell jobs (`run:` only, no package managers) | supported (generic) |
254
+ | Node.js / Go / Rust / Java / .NET | detected, reported as *unsupported* (roadmap v0.3) |
255
+ | Failing step is a third-party `uses:` action | reported as *unsupported* |
256
+
257
+ Replico understands checkout/setup actions, dependency install commands
258
+ (`pip install`, `-r` requirements, `pip install -e .`), `python -m pytest`,
259
+ environment blocks, `working-directory`, `defaults.run.shell`, `strategy`
260
+ matrices, and `${{ matrix.* }}` / basic `${{ github.* }}` expressions.
261
+ Secrets referenced as `${{ secrets.X }}` are never fetched; steps that
262
+ genuinely require them will fail locally in a deterministic way and Replico
263
+ will say so.
264
+
265
+ ## Result states
266
+
267
+ Replico distinguishes — and never conflates:
268
+
269
+ | State | Meaning |
270
+ | --- | --- |
271
+ | `reproduced` | the failing command failed locally **and** the failure signature (failing test id / error category) matches CI |
272
+ | `partially_reproduced` | a failure occurred locally but its identity could not be confirmed against CI, **or** the local run passed under materially different conditions |
273
+ | `not_reproduced` | the failing command passed locally under adequate environment parity |
274
+ | `unsupported` | Replico does not yet know how to reproduce this workflow |
275
+
276
+ Verbal results are matched by stable exit codes:
277
+
278
+ | Code | Meaning |
279
+ | --- | --- |
280
+ | `0` | reproduction succeeded — nothing is failing locally (verdict `not_reproduced`; a `rerun` that now passes) |
281
+ | `1` | reproduced failure still exists (verdict `reproduced`; a `rerun` that still fails) |
282
+ | `2` | could not reproduce (blocked, or `partially_reproduced` without a local failure) |
283
+ | `3` | invalid input (bad URL, unknown `--job`, missing args) |
284
+ | `4` | authentication problem (private repo without a usable token) |
285
+ | `5` | unsupported workflow |
286
+ | `6` | environment/setup problem (missing tool, venv/Docker failure) |
287
+ | `70` | internal error |
288
+
289
+ ## Environment parity
290
+
291
+ Replico fingerprints your machine (`replico env`) and compares it with the CI
292
+ job:
293
+
294
+ ```text
295
+ ENVIRONMENT DIFFERENCES
296
+ ✓ OS Windows 10/11: CI ubuntu-24.04 ← mismatch would be ✗ / a Docker hint
297
+ ✗ Python 3.13: using 3.12.4
298
+ ✓ git
299
+ ✓ dependencies
300
+ ✓ environment variables
301
+ Environment parity: 72% (estimate — not a guarantee)
302
+ ```
303
+
304
+ Parity is a transparent, weighted heuristic (OS, Python version, isolation,
305
+ git, dependencies, env vars) — not a claim of byte-for-byte parity with
306
+ GitHub's runner images. When parity is low and the local run passed, Replico
307
+ will not let you claim the failure is gone.
308
+
309
+ ## Isolation
310
+
311
+ * **Local (default, Python jobs):** dependencies are installed into a virtual
312
+ environment under `.replico/venv`, never into your global environment.
313
+ * **`--docker`:** the repository is mounted into a matching image
314
+ (`python:3.13-slim`, `ubuntu:24.04`, …) — the closest match for Linux
315
+ runners and the recommended mode when CI ran on a different OS.
316
+ * Automatic mode picks Docker when the CI runner OS (or requested Python
317
+ version) is not available locally and Docker is running.
318
+
319
+ Replico never runs `sudo`, administrator commands or destructive filesystem
320
+ operations without explicit confirmation. Commands extracted from workflow
321
+ files are audited first (`replico/security/guard.py`); risky ones require
322
+ `--yes` or an interactive confirm, and elevation is never performed for you.
323
+
324
+ ## Security
325
+
326
+ Replico is local-first and privacy-conscious:
327
+
328
+ * Network access is limited to the GitHub API (plus dependency downloads the
329
+ workflow itself requests). **No source code, logs, environment values or
330
+ artifacts are uploaded anywhere. There is no telemetry.**
331
+ * Tokens come from `GITHUB_TOKEN` / `GH_TOKEN` / the GitHub CLI and travel
332
+ only in the `Authorization` header of API requests. They are never logged,
333
+ displayed or saved.
334
+ * **SecretShield** (`secretshield>=0.4.2`) is used wherever sensitive content
335
+ could appear:
336
+ * CI logs and command output are scanned/redacted before display or
337
+ persistence (`redact`/`detect`),
338
+ * `secretshield.enable()` protects `stdout`/`stderr` and the logging module
339
+ as a last line of defense,
340
+ * `replico/security/redaction.py` is the single adapter between Replico and
341
+ SecretShield; Replico adds *literal* known-secret redaction (values from
342
+ your environment) on top, because SecretShield is pattern/entropy based
343
+ and cannot know that a low-entropy string is your password.
344
+ * Secret-like environment variables are shown as `NAME = present` — never
345
+ their values — in `replico env`, fingerprints, JSON output, `--debug`, and
346
+ everything saved under `.replico/`.
347
+ * Environment values are kept out of child-process environments unless they
348
+ are workflow literals that CI itself would set; `${{ secrets.* }}` is never
349
+ resolved or injected.
350
+ * Malicious inputs are handled defensively: YAML is parsed with a
351
+ budgeted/memoized engine (alias-expansion bombs are neutralized),
352
+ repository/job names are validated before touching paths or URLs, command
353
+ lines are audited, subprocesses are spawned without `shell=True` for
354
+ Replico's own commands, and env var names are validated.
355
+ * Workflow YAML is stored redacted under `.replico/`; see
356
+ `replico/security/` and the tests in `tests/` for the details.
357
+
358
+ ## Privacy
359
+
360
+ Your repository stays on your machine. Replico makes no network calls beyond
361
+ GitHub API requests that are required to read the run, its logs and its
362
+ workflow file, plus whatever the workflow itself runs (dependency installs).
363
+ There is no Replico server, no account, and telemetry is not collected — if
364
+ telemetry is ever introduced it will be opt-in only.
365
+
366
+ ## Limitations (honest)
367
+
368
+ * Replico does **not** clone GitHub's runner images. Tools preinstalled on
369
+ GitHub-hosted runners (compilers, system libraries, caches) are generally
370
+ absent locally; parity numbers reflect that.
371
+ * Only `run:` steps are replayed. Third-party actions cannot be executed
372
+ locally without their container/runtime.
373
+ * v0.1 covers Python workflows well and plain shell jobs; Node/Go/Rust are
374
+ detected and reported as unsupported rather than half-executed.
375
+ * Log analysis is heuristic. When Replico cannot extract a confident failure
376
+ signature it says so instead of guessing.
377
+ * Multi-line steps are replayed as one script (matching GitHub's behavior)
378
+ with the shell GitHub would use (`bash -eo pipefail`, pwsh on Windows).
379
+
380
+ ## Architecture
381
+
382
+ ```text
383
+ replico/
384
+ ├── cli.py argparse entry point, exit-code mapping
385
+ ├── flows.py reproduce / rerun orchestration
386
+ ├── pipeline.py run → plan → execute → verdict engine helpers
387
+ ├── cmds.py status / diff / env / clean / config / capture
388
+ ├── config.py .replico.toml (optional) + defaults
389
+ ├── ui.py safe console output (rich, sanitized, JSON mode)
390
+ ├── errors.py exceptions bound to stable exit codes
391
+ ├── github/ URL parsing, REST client (token-safe), job/step models
392
+ ├── workflow/ bomb-safe YAML parser, workflow model, job matcher,
393
+ │ environment/dependency detection
394
+ ├── environments/ ecosystem adapters (base, python), fingerprinting
395
+ ├── execution/ shell runner, Docker isolation
396
+ ├── analysis/ log analysis (500 lines → 12 relevant), classifier
397
+ ├── storage/ .replico/ store (redacted artifacts)
398
+ └── security/ SecretShield adapter, sanitizer, command/path guards
399
+ ```
400
+
401
+ Ecosystems plug in behind `EcosystemAdapter`:
402
+
403
+ ```python
404
+ class EcosystemAdapter(ABC):
405
+ def detect(self, analysis: JobAnalysis) -> EcosystemDetection: ...
406
+ # see environments/base.py — Node (planned v0.3) already registers
407
+ ```
408
+
409
+ ## Development
410
+
411
+ ```bash
412
+ pip install -e ".[dev]"
413
+ pytest # offline test suite (mocked GitHub)
414
+ ruff check . && ruff format --check .
415
+ mypy src/replico
416
+ python -m build # package validation
417
+ ```
418
+
419
+ ## Replico's own CI
420
+
421
+ `.github/workflows/ci.yml` tests Replico itself on Windows/Ubuntu/macOS and
422
+ Python 3.11–3.14, running tests, lint, type checks, build and package
423
+ validation. Dogfooding goal: Replico should eventually reproduce its own CI
424
+ failures (`replico capture` in a `if: failure()` step is the first step).
425
+
426
+ ## Roadmap
427
+
428
+ * **v0.1 (this release)** — GitHub Actions (public repos), failed job/step
429
+ detection, Python reproduction, honest verdicts, `.replico/`, Windows /
430
+ Linux / macOS, offline test suite, SecretShield integration.
431
+ * **v0.2** — private repos everywhere (already works with a token), richer
432
+ log analysis, `rerun`/`status`/JSON polish, better environment
433
+ comparison, Docker auto-detection improvements.
434
+ * **v0.3** — Node.js, Go, Rust adapters, matrix/multi-job refinements,
435
+ deeper failure classification.
436
+ * **v0.4+** — GitHub Action, PR comments, reproduction artifacts, local
437
+ failure history, IDE integrations.
438
+
439
+ ## Contributing
440
+
441
+ Issues and pull requests welcome. Before contributing, read the security
442
+ model (`replico/security/`) — secret safety is non-negotiable. All tests must
443
+ run offline; GitHub interactions are mocked.
444
+
445
+ ## License
446
+
447
+ MIT — see [LICENSE](LICENSE).