opsscript-gate 0.4.0__tar.gz → 0.5.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 (50) hide show
  1. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/.github/workflows/test.yml +15 -2
  2. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/CHANGELOG.md +37 -0
  3. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/PKG-INFO +117 -12
  4. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/README.md +116 -11
  5. opsscript_gate-0.5.0/README.zh-CN.md +67 -0
  6. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/SECURITY.md +6 -4
  7. opsscript_gate-0.5.0/action.yml +130 -0
  8. opsscript_gate-0.5.0/docs/configuration.md +115 -0
  9. opsscript_gate-0.5.0/docs/troubleshooting.md +67 -0
  10. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/pyproject.toml +1 -1
  11. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/src/opsscript_gate/__init__.py +1 -1
  12. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/src/opsscript_gate/cli.py +89 -4
  13. opsscript_gate-0.5.0/src/opsscript_gate/config.py +78 -0
  14. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/src/opsscript_gate/discovery.py +16 -3
  15. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/src/opsscript_gate/reporter.py +151 -32
  16. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/src/opsscript_gate/runner.py +274 -120
  17. opsscript_gate-0.5.0/tests/test_action_contract.py +22 -0
  18. opsscript_gate-0.5.0/tests/test_onboarding.py +133 -0
  19. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/tests/test_runner.py +450 -1
  20. opsscript_gate-0.4.0/action.yml +0 -90
  21. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/.gitattributes +0 -0
  22. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  23. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/.github/ISSUE_TEMPLATE/distro_request.yml +0 -0
  24. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  25. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/.github/assets/social-preview.png +0 -0
  26. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/.github/pull_request_template.md +0 -0
  27. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/.github/workflows/demo.yml +0 -0
  28. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/.github/workflows/release.yml +0 -0
  29. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/.gitignore +0 -0
  30. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/CONTRIBUTING.md +0 -0
  31. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/LICENSE +0 -0
  32. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/ROADMAP.md +0 -0
  33. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/docs/promotion/awesome-lists-pr.md +0 -0
  34. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/docs/promotion/juejin-zhihu-article.md +0 -0
  35. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/docs/promotion/reddit-devops.md +0 -0
  36. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/docs/promotion/show-hn.md +0 -0
  37. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/docs/promotion/v2ex-post.md +0 -0
  38. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/examples/alpine-incompatibility/README.md +0 -0
  39. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/examples/alpine-incompatibility/apt_dependency.sh +0 -0
  40. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/examples/basic/README.md +0 -0
  41. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/examples/basic/clean_setup.sh +0 -0
  42. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/examples/github-actions/README.md +0 -0
  43. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/examples/github-actions/workflow.yml +0 -0
  44. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/examples/interactive-hang/README.md +0 -0
  45. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/examples/interactive-hang/prompt_confirm.sh +0 -0
  46. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/src/opsscript_gate/models.py +0 -0
  47. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/src/opsscript_gate/remediation.py +0 -0
  48. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/tests/fixtures/fail_deps.sh +0 -0
  49. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/tests/fixtures/fail_hang.sh +0 -0
  50. {opsscript_gate-0.4.0 → opsscript_gate-0.5.0}/tests/fixtures/pass_basic.sh +0 -0
@@ -8,7 +8,11 @@ on:
8
8
 
9
9
  jobs:
10
10
  test:
11
- runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ os: [ubuntu-latest, windows-latest]
14
+ python: ['3.10', '3.12']
15
+ runs-on: ${{ matrix.os }}
12
16
  steps:
13
17
  - name: Checkout Code
14
18
  uses: actions/checkout@v7
@@ -16,7 +20,7 @@ jobs:
16
20
  - name: Set up Python
17
21
  uses: actions/setup-python@v7
18
22
  with:
19
- python-version: '3.12'
23
+ python-version: ${{ matrix.python }}
20
24
 
21
25
  - name: Install Dependencies
22
26
  run: |
@@ -50,3 +54,12 @@ jobs:
50
54
  - name: Run Docker Integration Tests
51
55
  run: |
52
56
  pytest -v -m integration
57
+
58
+ - name: Exercise the local composite Action
59
+ uses: ./
60
+ with:
61
+ script-path: tests/fixtures/pass_basic.sh
62
+ preset: minimal
63
+ network: none
64
+ output: action-report.json
65
+ format: json
@@ -1,10 +1,47 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.0] - 2026-09-24
4
+
5
+ ### Added
6
+ - `init`: non-overwriting project configuration and GitHub workflow generation.
7
+ - Shared, strictly validated `.opsscript-gate.json` settings; explicit CLI/Action inputs win.
8
+ - Docker-free `--dry-run` plans in text or JSON, including execution counts.
9
+ - `minimal` and `ubuntu` distribution presets, repeatable exclusion globs and configurable discovery limit.
10
+ - `--output` report artifacts, including failure reports; source/config overwrite protection.
11
+ - Action inputs for configuration, presets, exclusions, preview and saved reports.
12
+ - Chinese quickstart, configuration guide and Ubuntu/Alpine troubleshooting guide.
13
+
14
+ ### Fixed
15
+ - Discovery now fails on overflow instead of silently testing only the first 20 scripts.
16
+ - Discovery skips symlinks and non-files and rejects shebang prefix collisions.
17
+ - CLI rejects non-positive limits and unsupported network modes before execution.
18
+ - Default Action inputs no longer override a user's project configuration.
19
+
20
+ ### Migration
21
+ - Repositories with more than 20 discovered scripts must exclude unwanted scripts or raise `max_scripts`.
22
+ - Auto-discovery no longer follows file symlinks; explicit script paths remain supported.
23
+ - Default settings without a configuration file remain compatible with v0.4.1 and earlier.
24
+ - Generated workflows use the v0.5.0 release tag.
25
+
3
26
  All notable changes to this project will be documented in this file.
4
27
 
5
28
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
29
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
30
 
31
+ ## [0.4.1] - 2026-09-19
32
+
33
+ ### Fixed & Hardened
34
+ - **Bounded container log capture**: Stream-captures container logs using an immediate rolling byte buffer capped at 256 KiB (`MAX_CAPTURED_LOG_BYTES`) and tail limited to 500 lines (`MAX_LOG_TAIL_LINES`), bounding retained container log data to reduce memory-exhaustion risk from runaway output.
35
+ - **Workflow command neutralization**: Neutralizes untrusted container logs starting with `::` into `[container] ::` to prevent malicious scripts from forging GitHub Actions workflow commands in Runner consoles, while keeping OpsScript Gate's own annotations intact.
36
+ - **Terminal ANSI and control character sanitization**: Strips ANSI escape sequences (CSI, OSC, 2-byte escapes), removes dangerous C0 control characters and DEL, and normalizes CRLF and standalone carriage returns (`\r`) to defeat terminal line overwrite and status spoofing, while preserving printable Unicode.
37
+ - **Context-sensitive Markdown and HTML injection defense**: Implemented dedicated escaping (`escape_inline_code`, `escape_markdown_text`, `escape_markdown_table_cell`, `escape_html_text`, `format_safe_code_fence`) to prevent table cell tearing, HTML container breakout (`</details>`), premature code fence closure, and structural Markdown injection from runtime-derived values.
38
+ - **Bounded streaming script preparation**: Stream-normalizes CRLF in bounded 64 KiB chunks (`SCRIPT_READ_CHUNK_SIZE`), properly handling boundary-split CRLF while preserving lone CR bytes without whole-file memory allocation. Bounded shebang parsing to 4096 bytes (`MAX_SHEBANG_BYTES`).
39
+ - **Parallel matrix script preparation deduplication**: Pre-normalizes scripts once before parallel matrix execution, sharing the prepared file read-only across worker threads and centralizing cleanup to eliminate file locking and race conditions on Windows.
40
+ - **Shebang evaluation refactoring**: Deduplicated shebang resolution across `shebang` and `auto` modes via `_resolve_shell_command`.
41
+ - **Regression test coverage**: Added extensive adversarial and edge-case tests covering oversized streams, boundary splits, workflow spoofing, Markdown injections, and error branches.
42
+
43
+ ---
44
+
8
45
  ## [0.4.0] - 2026-09-19
9
46
 
10
47
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: opsscript-gate
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Run Linux shell scripts across Debian, Ubuntu and Alpine before release to catch runtime compatibility failures
5
5
  Project-URL: Homepage, https://github.com/Mresyzz/opsscript-gate
6
6
  Project-URL: Repository, https://github.com/Mresyzz/opsscript-gate
@@ -26,7 +26,9 @@ Provides-Extra: test
26
26
  Requires-Dist: pytest>=8.0.0; extra == 'test'
27
27
  Description-Content-Type: text/markdown
28
28
 
29
- # OpsScript Gate
29
+ # OpsScript Gate — shell script compatibility testing
30
+
31
+ [简体中文](README.zh-CN.md) · [Configuration](docs/configuration.md) · [Troubleshooting](docs/troubleshooting.md)
30
32
 
31
33
  > **ShellCheck tells you if your script looks portable. OpsScript Gate checks if it actually runs there.**
32
34
 
@@ -41,8 +43,32 @@ Description-Content-Type: text/markdown
41
43
 
42
44
  **OpsScript Gate** is a drop-in runtime compatibility gate for Linux shell scripts. It executes your scripts inside unprivileged Debian, Ubuntu, and Alpine containers before merge, catching environment-specific runtime failures, missing interpreters, and package-manager assumptions that static analysis cannot detect.
43
45
 
46
+ It is designed for shell testing, portable shell validation, Bash/POSIX compatibility checks, and cross-distro CI where syntax-only tooling is not enough.
47
+
44
48
  ---
45
49
 
50
+ ## New in v0.5.0
51
+
52
+ Preview exactly what will run, keep local and CI settings together, and exclude test
53
+ fixtures before executing scripts. These controls are included in v0.5.0. Projects on
54
+ v0.4.1 and earlier do not include them.
55
+
56
+ ```bash
57
+ opsscript-gate init
58
+ opsscript-gate run --dry-run
59
+ opsscript-gate run --format json --output reports/compatibility.json
60
+ ```
61
+
62
+ `init` creates `.opsscript-gate.json` and a GitHub Actions workflow without replacing
63
+ existing files. Review its exclusions (`tests/*`, `examples/*`) and offline network
64
+ default before running. No Docker is required for `init` or `--dry-run`.
65
+
66
+ Useful for standalone installers, container entrypoints and release scripts that
67
+ must work on both GNU/Linux and Alpine/BusyBox. Each script runs independently;
68
+ repository files, sibling scripts and project dependencies are **not** mounted.
69
+ See [configuration and migration](docs/configuration.md) and
70
+ [why a shell script works on Ubuntu but fails on Alpine](docs/troubleshooting.md).
71
+
46
72
  ## ⚡ 30-Second Quickstart
47
73
 
48
74
  ### In GitHub Actions (Zero Config)
@@ -58,15 +84,15 @@ jobs:
58
84
  runs-on: ubuntu-latest
59
85
  steps:
60
86
  - uses: actions/checkout@v7
61
- - uses: Mresyzz/opsscript-gate@v0.4.0
87
+ - uses: Mresyzz/opsscript-gate@v0.5.0
62
88
  ```
63
89
 
64
- > **Zero Config**: If `script-path` is omitted, OpsScript Gate automatically discovers shell scripts in your repository and tests them concurrently!
90
+ > **Zero Config**: If `script-path` is omitted, OpsScript Gate automatically discovers shell scripts in your repository. Scripts run sequentially, with concurrent distribution checks for each script. Preview discovery before running unfamiliar repositories.
65
91
 
66
92
  Or test a specific script with custom execution modes:
67
93
 
68
94
  ```yaml
69
- - uses: Mresyzz/opsscript-gate@v0.4.0
95
+ - uses: Mresyzz/opsscript-gate@v0.5.0
70
96
  with:
71
97
  script-path: scripts/install.sh
72
98
  shell: auto
@@ -88,6 +114,35 @@ opsscript-gate run ./scripts/install.sh
88
114
  opsscript-gate run
89
115
  ```
90
116
 
117
+ ### Try a Real Failure in 30 Seconds
118
+
119
+ Create `install.sh`:
120
+
121
+ ```sh
122
+ #!/bin/sh
123
+ set -e
124
+ apt-get --version
125
+ ```
126
+
127
+ Then run:
128
+
129
+ ```bash
130
+ opsscript-gate run ./install.sh
131
+ ```
132
+
133
+ A typical result across the default matrix looks like this:
134
+
135
+ ```text
136
+ Debian 12 PASS
137
+ Ubuntu 22.04 PASS
138
+ Ubuntu 24.04 PASS
139
+ Alpine 3.20 FAIL
140
+
141
+ apt-get: not found
142
+ ```
143
+
144
+ The script is valid shell, but the runtime environment is incompatible. That is exactly the class of failure OpsScript Gate is designed to catch.
145
+
91
146
  ---
92
147
 
93
148
  ## 🎯 Reviewer-First Experience: What It Produces
@@ -190,7 +245,10 @@ sh: line 4: apt-get: not found
190
245
 
191
246
  ## 🔒 Security Boundaries & Hardened Isolation
192
247
 
193
- OpsScript Gate is not a security sandbox for untrusted code. Containers may run as the image's default user, and Docker containers still share the host kernel.
248
+ OpsScript Gate is a runtime compatibility testing tool, **not a security sandbox for hostile or fully untrusted code**. Containers still share the host kernel, so target scripts should be treated accordingly.
249
+
250
+ <details>
251
+ <summary><strong>View security boundaries and runtime hardening details</strong></summary>
194
252
 
195
253
  OpsScript Gate applies conservative, restricted container defaults when running scripts:
196
254
 
@@ -208,14 +266,37 @@ OpsScript Gate applies conservative, restricted container defaults when running
208
266
  4. **Anti-Hang Deadlock Defense**:
209
267
  - Disables TTY and stdin (`stdin_open=False`, `tty=False`).
210
268
  - Disconnects standard input: `/bin/sh -c "... /tmp/target_script.sh </dev/null"`.
211
- - Injects `DEBIAN_FRONTEND=noninteractive` and `CI=true`. Interactive prompts (`read -p`) fail immediately instead of hanging CI runners.
269
+ - Injects `DEBIAN_FRONTEND=noninteractive` and `CI=true`. Interactive prompts (`read -p`) fail instead of hanging CI runners.
212
270
  5. **Hard Timeout & Container Cleanup**:
213
271
  - Enforces configurable timeout (default: 60s). Timed-out containers are sent `SIGKILL` and marked `TIMED_OUT`.
214
272
  - Container removal is attempted from a `finally` block in normal, failure, and timeout execution paths.
215
- 6. **Command Injection Defense**:
216
- - Workflow commands (`::error`) apply strict percent-encoding for all properties and message bodies, preventing unclassified container logs from injecting GitHub Actions workflow commands.
217
- 7. **Windows CRLF Defense**:
218
- - Automatically detects and normalizes carriage returns (`\r\n` -> `\n`) before container execution, preventing false `\r: command not found` errors.
273
+ 6. **Bounded Output & Memory Protection**:
274
+ - Captures container logs using a rolling byte buffer capped at 256 KiB (`MAX_CAPTURED_LOG_BYTES`) and a 500-line tail limit (`MAX_LOG_TAIL_LINES`) to reduce memory-exhaustion risk from runaway output.
275
+ 7. **Untrusted Log Neutralization & Terminal Defense**:
276
+ - Neutralizes line-leading workflow commands (`[container] ::`) to prevent forged GitHub Actions annotations in CI runners.
277
+ - Strips ANSI escape sequences and dangerous C0 control characters, and normalizes carriage returns (`\r`) to defeat terminal line-overwrite spoofing.
278
+ - Employs context-sensitive escaping (`escape_inline_code`, `escape_markdown_text`, `escape_markdown_table_cell`, `escape_html_text`, `format_safe_code_fence`) to protect Step Summary output contexts.
279
+ 8. **Command Injection Defense**:
280
+ - OpsScript Gate's own annotations (`::error`) apply strict percent-encoding for workflow-command fields and message bodies.
281
+ 9. **Bounded Streaming CRLF & Shebang Defense**:
282
+ - Stream-normalizes CRLF in 64 KiB chunks and bounds shebang parsing to 4096 bytes without whole-file memory allocation.
283
+ - Pre-normalizes scripts once before parallel matrix runs, sharing a read-only prepared path across worker threads.
284
+
285
+ For the complete supported-version policy and vulnerability reporting guidance, see [SECURITY.md](SECURITY.md).
286
+
287
+ </details>
288
+
289
+ ### When NOT to use OpsScript Gate
290
+
291
+ OpsScript Gate is intentionally focused on Linux shell runtime compatibility. It is not designed for:
292
+
293
+ - executing hostile or fully untrusted third-party scripts
294
+ - kernel-level or privileged behavior testing
295
+ - replacing full integration or end-to-end test suites
296
+ - validating macOS or Windows behavior
297
+ - proving that a script is secure
298
+
299
+ Use it when you want to know whether a shell script actually runs across the supported Linux distributions.
219
300
 
220
301
  ---
221
302
 
@@ -269,7 +350,23 @@ usage: opsscript-gate run [-h] [--matrix MATRIX] [-j JOBS] [--timeout TIMEOUT]
269
350
 
270
351
  ---
271
352
 
272
- ## 🐶 Dogfooding
353
+ ### v0.5.0 project controls
354
+
355
+ | Option | Purpose |
356
+ | :--- | :--- |
357
+ | `init` | Generate configuration and a GitHub workflow without overwriting files |
358
+ | `--dry-run` | Preview selected scripts, images and total executions without Docker |
359
+ | `--config PATH` | Load a specific JSON configuration file |
360
+ | `--preset minimal` | Debian + Alpine (two container runs per script) |
361
+ | `--preset ubuntu` | Ubuntu 22.04 + 24.04 |
362
+ | `--exclude 'tests/*'` | Exclude a repository-relative glob; repeatable |
363
+ | `--max-scripts 50` | Raise the default discovery limit of 20; overflow is an error |
364
+ | `--output report.json` | Save the selected output format, including failed test reports |
365
+
366
+ Explicit CLI options override project settings. Explicit script paths bypass discovery
367
+ and its exclusions. See [complete configuration semantics](docs/configuration.md).
368
+
369
+ ## 🌍 Real-World Usage
273
370
 
274
371
  OpsScript Gate is actively used in [`Mresyzz/linux-dev-bootstrap`](https://github.com/Mresyzz/linux-dev-bootstrap) to validate `install.sh` across the default Debian, Ubuntu, and Alpine matrix in GitHub Actions.
275
372
 
@@ -296,6 +393,14 @@ pytest -v
296
393
 
297
394
  ---
298
395
 
396
+ ## 💛 If OpsScript Gate Helps
397
+
398
+ If OpsScript Gate catches a compatibility problem in one of your scripts, consider starring the repository so other shell and DevOps maintainers can find it too.
399
+
400
+ Bug reports, real-world compatibility cases, and pull requests are especially welcome.
401
+
402
+ ---
403
+
299
404
  ## 📄 License
300
405
 
301
406
  OpsScript Gate is licensed under the [MIT License](LICENSE).
@@ -1,4 +1,6 @@
1
- # OpsScript Gate
1
+ # OpsScript Gate — shell script compatibility testing
2
+
3
+ [简体中文](README.zh-CN.md) · [Configuration](docs/configuration.md) · [Troubleshooting](docs/troubleshooting.md)
2
4
 
3
5
  > **ShellCheck tells you if your script looks portable. OpsScript Gate checks if it actually runs there.**
4
6
 
@@ -13,8 +15,32 @@
13
15
 
14
16
  **OpsScript Gate** is a drop-in runtime compatibility gate for Linux shell scripts. It executes your scripts inside unprivileged Debian, Ubuntu, and Alpine containers before merge, catching environment-specific runtime failures, missing interpreters, and package-manager assumptions that static analysis cannot detect.
15
17
 
18
+ It is designed for shell testing, portable shell validation, Bash/POSIX compatibility checks, and cross-distro CI where syntax-only tooling is not enough.
19
+
16
20
  ---
17
21
 
22
+ ## New in v0.5.0
23
+
24
+ Preview exactly what will run, keep local and CI settings together, and exclude test
25
+ fixtures before executing scripts. These controls are included in v0.5.0. Projects on
26
+ v0.4.1 and earlier do not include them.
27
+
28
+ ```bash
29
+ opsscript-gate init
30
+ opsscript-gate run --dry-run
31
+ opsscript-gate run --format json --output reports/compatibility.json
32
+ ```
33
+
34
+ `init` creates `.opsscript-gate.json` and a GitHub Actions workflow without replacing
35
+ existing files. Review its exclusions (`tests/*`, `examples/*`) and offline network
36
+ default before running. No Docker is required for `init` or `--dry-run`.
37
+
38
+ Useful for standalone installers, container entrypoints and release scripts that
39
+ must work on both GNU/Linux and Alpine/BusyBox. Each script runs independently;
40
+ repository files, sibling scripts and project dependencies are **not** mounted.
41
+ See [configuration and migration](docs/configuration.md) and
42
+ [why a shell script works on Ubuntu but fails on Alpine](docs/troubleshooting.md).
43
+
18
44
  ## ⚡ 30-Second Quickstart
19
45
 
20
46
  ### In GitHub Actions (Zero Config)
@@ -30,15 +56,15 @@ jobs:
30
56
  runs-on: ubuntu-latest
31
57
  steps:
32
58
  - uses: actions/checkout@v7
33
- - uses: Mresyzz/opsscript-gate@v0.4.0
59
+ - uses: Mresyzz/opsscript-gate@v0.5.0
34
60
  ```
35
61
 
36
- > **Zero Config**: If `script-path` is omitted, OpsScript Gate automatically discovers shell scripts in your repository and tests them concurrently!
62
+ > **Zero Config**: If `script-path` is omitted, OpsScript Gate automatically discovers shell scripts in your repository. Scripts run sequentially, with concurrent distribution checks for each script. Preview discovery before running unfamiliar repositories.
37
63
 
38
64
  Or test a specific script with custom execution modes:
39
65
 
40
66
  ```yaml
41
- - uses: Mresyzz/opsscript-gate@v0.4.0
67
+ - uses: Mresyzz/opsscript-gate@v0.5.0
42
68
  with:
43
69
  script-path: scripts/install.sh
44
70
  shell: auto
@@ -60,6 +86,35 @@ opsscript-gate run ./scripts/install.sh
60
86
  opsscript-gate run
61
87
  ```
62
88
 
89
+ ### Try a Real Failure in 30 Seconds
90
+
91
+ Create `install.sh`:
92
+
93
+ ```sh
94
+ #!/bin/sh
95
+ set -e
96
+ apt-get --version
97
+ ```
98
+
99
+ Then run:
100
+
101
+ ```bash
102
+ opsscript-gate run ./install.sh
103
+ ```
104
+
105
+ A typical result across the default matrix looks like this:
106
+
107
+ ```text
108
+ Debian 12 PASS
109
+ Ubuntu 22.04 PASS
110
+ Ubuntu 24.04 PASS
111
+ Alpine 3.20 FAIL
112
+
113
+ apt-get: not found
114
+ ```
115
+
116
+ The script is valid shell, but the runtime environment is incompatible. That is exactly the class of failure OpsScript Gate is designed to catch.
117
+
63
118
  ---
64
119
 
65
120
  ## 🎯 Reviewer-First Experience: What It Produces
@@ -162,7 +217,10 @@ sh: line 4: apt-get: not found
162
217
 
163
218
  ## 🔒 Security Boundaries & Hardened Isolation
164
219
 
165
- OpsScript Gate is not a security sandbox for untrusted code. Containers may run as the image's default user, and Docker containers still share the host kernel.
220
+ OpsScript Gate is a runtime compatibility testing tool, **not a security sandbox for hostile or fully untrusted code**. Containers still share the host kernel, so target scripts should be treated accordingly.
221
+
222
+ <details>
223
+ <summary><strong>View security boundaries and runtime hardening details</strong></summary>
166
224
 
167
225
  OpsScript Gate applies conservative, restricted container defaults when running scripts:
168
226
 
@@ -180,14 +238,37 @@ OpsScript Gate applies conservative, restricted container defaults when running
180
238
  4. **Anti-Hang Deadlock Defense**:
181
239
  - Disables TTY and stdin (`stdin_open=False`, `tty=False`).
182
240
  - Disconnects standard input: `/bin/sh -c "... /tmp/target_script.sh </dev/null"`.
183
- - Injects `DEBIAN_FRONTEND=noninteractive` and `CI=true`. Interactive prompts (`read -p`) fail immediately instead of hanging CI runners.
241
+ - Injects `DEBIAN_FRONTEND=noninteractive` and `CI=true`. Interactive prompts (`read -p`) fail instead of hanging CI runners.
184
242
  5. **Hard Timeout & Container Cleanup**:
185
243
  - Enforces configurable timeout (default: 60s). Timed-out containers are sent `SIGKILL` and marked `TIMED_OUT`.
186
244
  - Container removal is attempted from a `finally` block in normal, failure, and timeout execution paths.
187
- 6. **Command Injection Defense**:
188
- - Workflow commands (`::error`) apply strict percent-encoding for all properties and message bodies, preventing unclassified container logs from injecting GitHub Actions workflow commands.
189
- 7. **Windows CRLF Defense**:
190
- - Automatically detects and normalizes carriage returns (`\r\n` -> `\n`) before container execution, preventing false `\r: command not found` errors.
245
+ 6. **Bounded Output & Memory Protection**:
246
+ - Captures container logs using a rolling byte buffer capped at 256 KiB (`MAX_CAPTURED_LOG_BYTES`) and a 500-line tail limit (`MAX_LOG_TAIL_LINES`) to reduce memory-exhaustion risk from runaway output.
247
+ 7. **Untrusted Log Neutralization & Terminal Defense**:
248
+ - Neutralizes line-leading workflow commands (`[container] ::`) to prevent forged GitHub Actions annotations in CI runners.
249
+ - Strips ANSI escape sequences and dangerous C0 control characters, and normalizes carriage returns (`\r`) to defeat terminal line-overwrite spoofing.
250
+ - Employs context-sensitive escaping (`escape_inline_code`, `escape_markdown_text`, `escape_markdown_table_cell`, `escape_html_text`, `format_safe_code_fence`) to protect Step Summary output contexts.
251
+ 8. **Command Injection Defense**:
252
+ - OpsScript Gate's own annotations (`::error`) apply strict percent-encoding for workflow-command fields and message bodies.
253
+ 9. **Bounded Streaming CRLF & Shebang Defense**:
254
+ - Stream-normalizes CRLF in 64 KiB chunks and bounds shebang parsing to 4096 bytes without whole-file memory allocation.
255
+ - Pre-normalizes scripts once before parallel matrix runs, sharing a read-only prepared path across worker threads.
256
+
257
+ For the complete supported-version policy and vulnerability reporting guidance, see [SECURITY.md](SECURITY.md).
258
+
259
+ </details>
260
+
261
+ ### When NOT to use OpsScript Gate
262
+
263
+ OpsScript Gate is intentionally focused on Linux shell runtime compatibility. It is not designed for:
264
+
265
+ - executing hostile or fully untrusted third-party scripts
266
+ - kernel-level or privileged behavior testing
267
+ - replacing full integration or end-to-end test suites
268
+ - validating macOS or Windows behavior
269
+ - proving that a script is secure
270
+
271
+ Use it when you want to know whether a shell script actually runs across the supported Linux distributions.
191
272
 
192
273
  ---
193
274
 
@@ -241,7 +322,23 @@ usage: opsscript-gate run [-h] [--matrix MATRIX] [-j JOBS] [--timeout TIMEOUT]
241
322
 
242
323
  ---
243
324
 
244
- ## 🐶 Dogfooding
325
+ ### v0.5.0 project controls
326
+
327
+ | Option | Purpose |
328
+ | :--- | :--- |
329
+ | `init` | Generate configuration and a GitHub workflow without overwriting files |
330
+ | `--dry-run` | Preview selected scripts, images and total executions without Docker |
331
+ | `--config PATH` | Load a specific JSON configuration file |
332
+ | `--preset minimal` | Debian + Alpine (two container runs per script) |
333
+ | `--preset ubuntu` | Ubuntu 22.04 + 24.04 |
334
+ | `--exclude 'tests/*'` | Exclude a repository-relative glob; repeatable |
335
+ | `--max-scripts 50` | Raise the default discovery limit of 20; overflow is an error |
336
+ | `--output report.json` | Save the selected output format, including failed test reports |
337
+
338
+ Explicit CLI options override project settings. Explicit script paths bypass discovery
339
+ and its exclusions. See [complete configuration semantics](docs/configuration.md).
340
+
341
+ ## 🌍 Real-World Usage
245
342
 
246
343
  OpsScript Gate is actively used in [`Mresyzz/linux-dev-bootstrap`](https://github.com/Mresyzz/linux-dev-bootstrap) to validate `install.sh` across the default Debian, Ubuntu, and Alpine matrix in GitHub Actions.
247
344
 
@@ -268,6 +365,14 @@ pytest -v
268
365
 
269
366
  ---
270
367
 
368
+ ## 💛 If OpsScript Gate Helps
369
+
370
+ If OpsScript Gate catches a compatibility problem in one of your scripts, consider starring the repository so other shell and DevOps maintainers can find it too.
371
+
372
+ Bug reports, real-world compatibility cases, and pull requests are especially welcome.
373
+
374
+ ---
375
+
271
376
  ## 📄 License
272
377
 
273
378
  OpsScript Gate is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,67 @@
1
+ # OpsScript Gate:Shell 脚本跨发行版运行检查
2
+
3
+ [English](README.md) · [配置说明](docs/configuration.md) · [常见问题](docs/troubleshooting.md)
4
+
5
+ 在 Debian、Ubuntu、Alpine 容器里实际运行 Shell 脚本,提前发现缺失命令、
6
+ Bash 依赖、包管理器假设和交互式阻塞。它与 ShellCheck 互补。
7
+
8
+ 适合独立安装脚本、容器入口脚本和发布脚本。每次只挂载目标脚本,
9
+ 不挂载整个仓库;依赖其他仓库文件的脚本需要现有项目测试配合。
10
+
11
+ ## 试用本次 v0.5.0 更新
12
+
13
+ v0.5.0 已作为正式版本发布;如果你要从源码验证当前分支,也可以执行:
14
+
15
+ ```bash
16
+ pip install -e .
17
+ opsscript-gate run examples/basic/clean_setup.sh --dry-run
18
+ ```
19
+
20
+ 在你希望接入检查的仓库根目录执行:
21
+
22
+ ```bash
23
+ opsscript-gate init
24
+ opsscript-gate run --dry-run
25
+ opsscript-gate run --format json --output reports/compatibility.json
26
+ ```
27
+
28
+ `init` 生成配置和 GitHub 工作流,不覆盖已有文件。工作流引用 `v0.5.0`。
29
+ 预览不需要 Docker;真实运行需要 Python 3.10+
30
+ 和可访问的 Linux Docker 引擎。
31
+
32
+ 生成的配置默认排除 `tests/*`、`examples/*`,使用 Debian + Alpine,尊重
33
+ 脚本 shebang,并关闭网络。请根据实际脚本审核配置;需要下载文件时启用
34
+ `--network bridge`。保留现有无配置行为:四个发行版、POSIX shell、bridge 网络。
35
+
36
+ ## 本轮改进
37
+
38
+ - 运行前展示脚本、镜像和总容器执行次数。
39
+ - 本地与 GitHub Actions 共用 `.opsscript-gate.json`。
40
+ - 排除规则、发行版预设、扫描数量限制。
41
+ - 超过扫描上限明确报错,避免只测前 20 个却误以为全部通过。
42
+ - 报告保存为 JSON、Markdown 或文本,失败时同样保留结果。
43
+ - 自动发现跳过符号链接,并严格校验配置与执行参数。
44
+
45
+ 返回码 `0` 表示全部通过,`1` 表示检查失败或发生错误。`--dry-run` 成功只表示
46
+ 执行计划生成成功。容器不是不可信代码的安全沙箱。
47
+
48
+ ## 示例:为什么 Alpine 上会失败
49
+
50
+ ```sh
51
+ #!/bin/sh
52
+ set -e
53
+ apt-get --version
54
+ ```
55
+
56
+ 这个脚本可以符合 POSIX 语法,但 Alpine 使用 `apk`,实际执行会暴露命令缺失。
57
+ 查看 [Ubuntu/Alpine 排错说明](docs/troubleshooting.md) 获取常见原因与修复方向。
58
+
59
+ ## 测试
60
+
61
+ ```bash
62
+ pip install -e '.[test]'
63
+ pytest -q -m 'not integration'
64
+ pytest -q -m integration
65
+ ```
66
+
67
+ 第二组需要 Docker。跳过容器测试不能算作完整集成验证。
@@ -7,8 +7,7 @@ Only the latest release of **OpsScript Gate** receives security updates and bug
7
7
  | Version | Supported |
8
8
  | :--- | :---: |
9
9
  | 0.4.x | :white_check_mark: |
10
- | 0.3.x | :white_check_mark: |
11
- | < 0.3.0 | :x: |
10
+ | < 0.4.0 | :x: |
12
11
 
13
12
  ---
14
13
 
@@ -32,9 +31,12 @@ Reports will be reviewed as maintainer availability allows.
32
31
 
33
32
  OpsScript Gate is not a security sandbox for untrusted code. Containers may run as the image's default user, and Docker containers still share the host kernel.
34
33
 
35
- OpsScript Gate applies conservative container defaults when running scripts:
34
+ OpsScript Gate applies conservative container defaults and hardened I/O boundaries when running scripts:
36
35
  - **Restricted Container Defaults**: Containers run with `privileged=False`, `cap_drop=["ALL"]`, and `security_opt=["no-new-privileges:true"]`.
37
36
  - **Resource Limits & Isolation**: Enforces memory caps (`--mem-limit`, default 256m), process table caps (`--pids-limit`, default 128), and optional network isolation (`--network none`).
38
37
  - **Read-Only Mounting**: Target scripts are mounted read-only (`:ro`). OpsScript Gate does not mount additional host filesystem paths into test containers.
39
38
  - **Resource Protection & Hard Timeout**: Containers are subject to hard timeouts (default 60s) with `SIGKILL` termination. Container removal is attempted from a `finally` block in normal, failure, and timeout execution paths.
40
- - **Workflow Command Injection Defense**: All GitHub Actions workflow commands (`::error`) apply strict percent-encoding for properties (`%`, `\r`, `\n`, `:`, `,`) and data (`%`, `\r`, `\n`), completely preventing command injection from unclassified container output.
39
+ - **Bounded Output & Rolling Buffer**: Container output capture is strictly bounded by tail lines (500) and byte caps (256 KiB) via an immediate rolling byte buffer, bounding retained container log data to reduce memory-exhaustion risk.
40
+ - **Untrusted Log Neutralization**: Container logs neutralize line-leading workflow commands (`[container] ::`), strip terminal ANSI/CSI/OSC sequences and dangerous C0 control characters, and apply context-sensitive Markdown/HTML escaping to protect Step Summaries.
41
+ - **Workflow Command Encoding**: OpsScript Gate's own GitHub Actions annotations (`::error`) apply strict percent-encoding for properties (`%`, `\r`, `\n`, `:`, `,`) and data (`%`, `\r`, `\n`), protecting GitHub Actions workflow-command fields against injection.
42
+ - **Bounded Streaming I/O**: Script preparation and line ending normalization operate in bounded 64 KiB chunks, and shebang line inspection is bounded to 4096 bytes.