opsscript-gate 0.5.0__tar.gz → 0.5.1__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 (49) hide show
  1. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/CHANGELOG.md +21 -18
  2. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/PKG-INFO +44 -41
  3. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/README.md +42 -39
  4. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/README.zh-CN.md +3 -3
  5. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/action.yml +1 -1
  6. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/docs/configuration.md +2 -2
  7. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/alpine-incompatibility/README.md +2 -1
  8. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/github-actions/README.md +2 -2
  9. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/github-actions/workflow.yml +3 -1
  10. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/pyproject.toml +2 -2
  11. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/__init__.py +1 -1
  12. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/config.py +1 -1
  13. opsscript_gate-0.5.0/docs/promotion/awesome-lists-pr.md +0 -59
  14. opsscript_gate-0.5.0/docs/promotion/juejin-zhihu-article.md +0 -144
  15. opsscript_gate-0.5.0/docs/promotion/reddit-devops.md +0 -56
  16. opsscript_gate-0.5.0/docs/promotion/show-hn.md +0 -67
  17. opsscript_gate-0.5.0/docs/promotion/v2ex-post.md +0 -68
  18. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.gitattributes +0 -0
  19. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  20. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/ISSUE_TEMPLATE/distro_request.yml +0 -0
  21. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  22. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/assets/social-preview.png +0 -0
  23. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/pull_request_template.md +0 -0
  24. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/workflows/demo.yml +0 -0
  25. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/workflows/release.yml +0 -0
  26. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/workflows/test.yml +0 -0
  27. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.gitignore +0 -0
  28. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/CONTRIBUTING.md +0 -0
  29. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/LICENSE +0 -0
  30. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/ROADMAP.md +0 -0
  31. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/SECURITY.md +0 -0
  32. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/docs/troubleshooting.md +0 -0
  33. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/alpine-incompatibility/apt_dependency.sh +0 -0
  34. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/basic/README.md +0 -0
  35. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/basic/clean_setup.sh +0 -0
  36. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/interactive-hang/README.md +0 -0
  37. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/interactive-hang/prompt_confirm.sh +0 -0
  38. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/cli.py +0 -0
  39. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/discovery.py +0 -0
  40. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/models.py +0 -0
  41. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/remediation.py +0 -0
  42. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/reporter.py +0 -0
  43. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/runner.py +0 -0
  44. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/tests/fixtures/fail_deps.sh +0 -0
  45. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/tests/fixtures/fail_hang.sh +0 -0
  46. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/tests/fixtures/pass_basic.sh +0 -0
  47. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/tests/test_action_contract.py +0 -0
  48. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/tests/test_onboarding.py +0 -0
  49. {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/tests/test_runner.py +0 -0
@@ -1,21 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.1] - 2026-09-30
4
+
5
+ ### Changed
6
+ - Published the current README and examples with the v0.5.1 Action tag.
7
+ - Updated package and Action metadata to describe the supported container matrix and runtime compatibility check.
8
+
3
9
  ## [0.5.0] - 2026-09-24
4
10
 
5
11
  ### 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.
12
+ - Added `opsscript-gate init` to create a config file and GitHub workflow without replacing existing files.
13
+ - Added `.opsscript-gate.json` with CLI and Action overrides.
14
+ - Added `--dry-run`, `minimal` and `ubuntu` presets, exclusion globs, and `--output` reports.
15
+ - Added configuration, Chinese quickstart, and Ubuntu/Alpine troubleshooting documentation.
13
16
 
14
17
  ### 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.
18
+ - Discovery stops at the first candidate beyond the configured limit instead of silently ignoring files.
19
+ - Discovery skips symlinks and non-files and checks shebangs without prefix matches.
20
+ - CLI validates limits and network modes before execution.
21
+ - Empty Action inputs no longer override project configuration.
19
22
 
20
23
  ### Migration
21
24
  - Repositories with more than 20 discovered scripts must exclude unwanted scripts or raise `max_scripts`.
@@ -31,8 +34,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
31
34
  ## [0.4.1] - 2026-09-19
32
35
 
33
36
  ### 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.
37
+ - Bounded container log capture with a 256 KiB rolling buffer and 500-line tail.
38
+ - Neutralized untrusted container logs beginning with `::` before they reach GitHub Actions.
36
39
  - **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
40
  - **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
41
  - **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`).
@@ -45,15 +48,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
45
48
  ## [0.4.0] - 2026-09-19
46
49
 
47
50
  ### Added
48
- - **High-confidence line-level GitHub Actions annotations**: Emits inline `::error` annotations with exact script line numbers directly on pull request file diffs.
51
+ - Line-level GitHub Actions annotations with script paths and line numbers.
49
52
  - **Workflow command injection defense**: Strictly escapes parameters (`%`, `\r`, `\n`, `:`, `,`) and message bodies (`%`, `\r`, `\n`) to eliminate command injection from unclassified container logs.
50
- - **Conservative distro-aware remediation hints**: Suggests testable, high-confidence remediation recommendations (`RemediationRule`) with conservative phrasing (`normally`, `may`, `consider`), covering:
53
+ - Distro-aware remediation hints for common mismatches, including:
51
54
  - Alpine package manager mismatches (`apt`/`apt-get` -> `apk`)
52
55
  - Debian/Ubuntu package manager mismatches (`apk` -> `apt-get`)
53
56
  - Alpine minimal missing Bash (`bash` -> consider POSIX sh or installing bash)
54
57
  - Missing network prerequisites (`curl`, `wget`)
55
58
  - Missing shebang interpreter paths.
56
- - **Re-engineered Compatibility Card**: Overhauled GitHub Actions Step Summary with an instant 5-second triage table and an expandable copy-pasteable Markdown snippet ready for PR descriptions and Issue comments.
59
+ - Updated the GitHub Actions Step Summary with a compatibility table and a Markdown result block.
57
60
  - **Parallel matrix execution (`--jobs` / `jobs`)**: Concurrently runs container checks using `ThreadPoolExecutor` (defaults to `min(2, matrix_size)`), strictly preserving matrix output order and ensuring reliable container cleanup.
58
61
  - **Hardened container resource limits**: Added `--mem-limit` (default: 256m) and `--pids-limit` (default: 128) flags and Action inputs to prevent runaway resource exhaustion.
59
62
  - **Configurable network isolation (`--network`)**: Supports `--network bridge` (default) and `--network none` for offline script execution.
@@ -70,7 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
70
73
 
71
74
  ### Added
72
75
  - Structured runtime failure diagnostics: detect common "command not found" failures across Linux distributions (Alpine/BusyBox ash, Debian/Ubuntu dash, Bash, and missing interpreter paths).
73
- - High-confidence dual-signal classification: requires exit code 127 and matching shell output to produce a `missing_command` diagnostic.
76
+ - Missing-command classification requires exit code 127 and matching shell output.
74
77
  - Additive `diagnostic` field in JSON report format (`SingleResult.to_dict()`) providing structured diagnostic information (backward-compatible for consumers tolerating additional fields).
75
78
  - Enhanced terminal table formatting and GitHub Actions Step Summary showing clear failure causes in the summary overview.
76
79
 
@@ -104,7 +107,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
104
107
  ### Added
105
108
  - Configured PyPI Trusted Publishing via GitHub Actions OIDC (`pypa/gh-action-pypi-publish`).
106
109
  - Added full PyPI package metadata, classifiers, and project URLs to `pyproject.toml`.
107
- - Ensured absolute asset URLs in `README.md` for seamless PyPI rendering.
110
+ - Ensured asset URLs in `README.md` render correctly on PyPI.
108
111
 
109
112
  ---
110
113
 
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: opsscript-gate
3
- Version: 0.5.0
4
- Summary: Run Linux shell scripts across Debian, Ubuntu and Alpine before release to catch runtime compatibility failures
3
+ Version: 0.5.1
4
+ Summary: Run Linux shell scripts in Debian, Ubuntu and Alpine containers to catch runtime compatibility failures before merge
5
5
  Project-URL: Homepage, https://github.com/Mresyzz/opsscript-gate
6
6
  Project-URL: Repository, https://github.com/Mresyzz/opsscript-gate
7
7
  Project-URL: Issues, https://github.com/Mresyzz/opsscript-gate/issues
@@ -30,7 +30,7 @@ Description-Content-Type: text/markdown
30
30
 
31
31
  [简体中文](README.zh-CN.md) · [Configuration](docs/configuration.md) · [Troubleshooting](docs/troubleshooting.md)
32
32
 
33
- > **ShellCheck tells you if your script looks portable. OpsScript Gate checks if it actually runs there.**
33
+ > ShellCheck analyzes shell syntax. OpsScript Gate executes scripts in Linux containers.
34
34
 
35
35
  [![CI](https://github.com/Mresyzz/opsscript-gate/actions/workflows/test.yml/badge.svg)](https://github.com/Mresyzz/opsscript-gate/actions/workflows/test.yml)
36
36
  [![Demo](https://github.com/Mresyzz/opsscript-gate/actions/workflows/demo.yml/badge.svg)](https://github.com/Mresyzz/opsscript-gate/actions/workflows/demo.yml)
@@ -41,17 +41,17 @@ Description-Content-Type: text/markdown
41
41
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
42
42
  [![Supported Distros](https://img.shields.io/badge/matrix-Debian%20%7C%20Ubuntu%20%7C%20Alpine-orange.svg)](#default-test-matrix)
43
43
 
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.
44
+ **OpsScript Gate** is a runtime compatibility checker for Linux shell scripts. It executes scripts inside unprivileged Debian, Ubuntu, and Alpine containers, catching environment-specific failures, missing interpreters, and package-manager assumptions that static analysis cannot detect.
45
45
 
46
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
47
 
48
48
  ---
49
49
 
50
- ## New in v0.5.0
50
+ ## Current release: v0.5.1
51
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.
52
+ The current release includes a project config file, a dry-run plan, presets, exclusions,
53
+ and saved reports. These controls were introduced in v0.5.0; projects on v0.4.1 and
54
+ earlier do not include them.
55
55
 
56
56
  ```bash
57
57
  opsscript-gate init
@@ -69,11 +69,14 @@ repository files, sibling scripts and project dependencies are **not** mounted.
69
69
  See [configuration and migration](docs/configuration.md) and
70
70
  [why a shell script works on Ubuntu but fails on Alpine](docs/troubleshooting.md).
71
71
 
72
- ## ⚡ 30-Second Quickstart
72
+ ## Quickstart
73
73
 
74
- ### In GitHub Actions (Zero Config)
74
+ ### In GitHub Actions
75
75
 
76
- Drop this minimal workflow into `.github/workflows/shell-compat.yml`:
76
+ Add this workflow to `.github/workflows/shell-compat.yml`:
77
+
78
+ You can copy the maintained example from [`examples/github-actions/workflow.yml`](examples/github-actions/workflow.yml)
79
+ and change the `script-path`, or use the minimal workflow below.
77
80
 
78
81
  ```yaml
79
82
  name: Shell Compatibility Gate
@@ -84,15 +87,16 @@ jobs:
84
87
  runs-on: ubuntu-latest
85
88
  steps:
86
89
  - uses: actions/checkout@v7
87
- - uses: Mresyzz/opsscript-gate@v0.5.0
90
+ - uses: Mresyzz/opsscript-gate@v0.5.1
88
91
  ```
89
92
 
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.
93
+ If `script-path` is omitted, the action discovers shell scripts in the repository.
94
+ Use `opsscript-gate run --dry-run` to inspect the selection before running it.
91
95
 
92
96
  Or test a specific script with custom execution modes:
93
97
 
94
98
  ```yaml
95
- - uses: Mresyzz/opsscript-gate@v0.5.0
99
+ - uses: Mresyzz/opsscript-gate@v0.5.1
96
100
  with:
97
101
  script-path: scripts/install.sh
98
102
  shell: auto
@@ -114,7 +118,7 @@ opsscript-gate run ./scripts/install.sh
114
118
  opsscript-gate run
115
119
  ```
116
120
 
117
- ### Try a Real Failure in 30 Seconds
121
+ ### Example: package-manager mismatch
118
122
 
119
123
  Create `install.sh`:
120
124
 
@@ -141,23 +145,23 @@ Alpine 3.20 FAIL
141
145
  apt-get: not found
142
146
  ```
143
147
 
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.
148
+ The script is valid shell, but it assumes a command that is not present in Alpine.
145
149
 
146
150
  ---
147
151
 
148
- ## 🎯 Reviewer-First Experience: What It Produces
152
+ ## CI output
149
153
 
150
- Every run of OpsScript Gate generates clean, actionable feedback right where developers and reviewers need it:
154
+ The action reports failures in two places:
151
155
 
152
- ### 1. Line-Level GitHub Annotations
153
- When a failure occurs, OpsScript Gate flags the exact script line on your Pull Request's **Files Changed** view with high-confidence diagnostics and conservative remediation hints:
156
+ ### GitHub annotations
157
+ Failures include the script path, line number, distribution, and a short diagnostic:
154
158
 
155
159
  ```text
156
160
  ::error file=scripts/setup.sh,line=4,title=OpsScript Gate: [alpine:3.20] command not found: apt-get::command not found: apt-get — Alpine normally uses apk instead of apt-get.
157
161
  ```
158
162
 
159
- ### 2. GitHub Step Summary Compatibility Card
160
- A beautifully formatted markdown summary is automatically posted to `$GITHUB_STEP_SUMMARY`:
163
+ ### Step summary
164
+ The action also writes a markdown matrix to `$GITHUB_STEP_SUMMARY`:
161
165
 
162
166
  ```markdown
163
167
  ## 🛡️ OpsScript Gate Compatibility Report
@@ -176,14 +180,14 @@ A beautifully formatted markdown summary is automatically posted to `$GITHUB_STE
176
180
  | `alpine:3.20` | ❌ FAIL | `127` | `0.19s` | ⚠️ Missing command: `apt-get` (line 4)<br>💡 *Alpine normally uses apk instead of apt-get.* |
177
181
 
178
182
  <details>
179
- <summary>📋 <b>Copyable Markdown (Click to expand & copy to PR / Issue)</b></summary>
183
+ <summary><b>Markdown result</b></summary>
180
184
  ...
181
185
  </details>
182
186
  ```
183
187
 
184
188
  ---
185
189
 
186
- ## 🔍 Example: What Static Analysis Misses
190
+ ## Example: runtime behavior that static analysis misses
187
191
 
188
192
  Consider this clean deployment script:
189
193
 
@@ -220,37 +224,38 @@ Failed Distributions - Output Snippets (last 15 lines):
220
224
  sh: line 4: apt-get: not found
221
225
  ```
222
226
 
223
- **Why it failed:** Alpine Linux is musl/BusyBox-based and uses `apk`, not `apt-get`. OpsScript Gate catches the missing command (`exit code 127`) and gives you the exact line number and conservative remediation hint before deployment.
227
+ **Why it failed:** Alpine uses `apk`, not `apt-get`. The report includes the missing command and its line number.
224
228
 
225
229
  ---
226
230
 
227
- ## 🛡️ Why OpsScript Gate?
231
+ ## Scope compared with other checks
228
232
 
229
- ### OpsScript Gate vs ShellCheck vs Custom CI Matrix
233
+ ### OpsScript Gate compared with ShellCheck and a custom CI matrix
230
234
 
231
235
  | Capability | OpsScript Gate | ShellCheck | Handwritten CI Matrix |
232
236
  | :--- | :---: | :---: | :---: |
233
237
  | **Runtime execution** | **Yes** | No (Static AST only) | Yes |
234
238
  | **Real distro environments** | **Yes (Debian, Ubuntu, Alpine)** | No | Yes |
235
239
  | **Preconfigured defaults** | **Yes** | Yes | Requires custom workflow configuration |
236
- | **Hardened container defaults** | **Built-in (`ro`, `cap_drop`, resource limits)** | N/A | User-defined |
240
+ | **Restricted container defaults** | **Built-in (`ro`, `cap_drop`, resource limits)** | N/A | User-defined |
237
241
  | **Anti-hang stdin protection** | **Built-in (`</dev/null`, noninteractive)** | No | User-defined |
238
- | **Line-Level Annotations & Hints** | **Built-in (Zero config)** | Static warnings | User-defined |
242
+ | **Line-Level Annotations & Hints** | **Built-in** | Static warnings | User-defined |
239
243
  | **Parallel Matrix Execution** | **Built-in (`--jobs`)** | N/A | Manual matrix config |
240
244
 
241
- - **ShellCheck** is indispensable for static analysis (syntax, quoting, SC warnings). OpsScript Gate complements it by testing actual execution behavior in real distributions.
242
- - **Handwritten CI Matrix** requires maintaining complex Docker configurations, volume mounts, timeout guards, and log parsers across every project. OpsScript Gate packages this into a single check.
245
+ ShellCheck checks shell syntax and common mistakes. OpsScript Gate runs the script in
246
+ real distributions. A custom CI matrix can do the same job, but each repository must
247
+ maintain its own images, mounts, timeouts, and report handling.
243
248
 
244
249
  ---
245
250
 
246
- ## 🔒 Security Boundaries & Hardened Isolation
251
+ ## Security boundaries and container isolation
247
252
 
248
253
  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
254
 
250
255
  <details>
251
256
  <summary><strong>View security boundaries and runtime hardening details</strong></summary>
252
257
 
253
- OpsScript Gate applies conservative, restricted container defaults when running scripts:
258
+ The runner uses restricted container defaults:
254
259
 
255
260
  1. **Restricted Container Defaults**:
256
261
  - Containers run with `privileged=False`.
@@ -300,7 +305,7 @@ Use it when you want to know whether a shell script actually runs across the sup
300
305
 
301
306
  ---
302
307
 
303
- ## 📦 Default Test Matrix
308
+ ## Default test matrix
304
309
 
305
310
  | Image | Distribution | Focus |
306
311
  | :--- | :--- | :--- |
@@ -313,7 +318,7 @@ Customize the matrix at any time via `--matrix` or Action input `matrix`.
313
318
 
314
319
  ---
315
320
 
316
- ## 🛠️ CLI Reference
321
+ ## CLI reference
317
322
 
318
323
  ```text
319
324
  usage: opsscript-gate run [-h] [--matrix MATRIX] [-j JOBS] [--timeout TIMEOUT]
@@ -366,9 +371,9 @@ usage: opsscript-gate run [-h] [--matrix MATRIX] [-j JOBS] [--timeout TIMEOUT]
366
371
  Explicit CLI options override project settings. Explicit script paths bypass discovery
367
372
  and its exclusions. See [complete configuration semantics](docs/configuration.md).
368
373
 
369
- ## 🌍 Real-World Usage
374
+ ## Downstream example
370
375
 
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.
376
+ [`Mresyzz/linux-dev-bootstrap`](https://github.com/Mresyzz/linux-dev-bootstrap) contains a workflow that validates `install.sh` across the default Debian, Ubuntu, and Alpine matrix.
372
377
 
373
378
  See the downstream workflow: [`.github/workflows/test.yml`](https://github.com/Mresyzz/linux-dev-bootstrap/blob/main/.github/workflows/test.yml).
374
379
 
@@ -393,11 +398,9 @@ pytest -v
393
398
 
394
399
  ---
395
400
 
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.
401
+ ## Feedback
399
402
 
400
- Bug reports, real-world compatibility cases, and pull requests are especially welcome.
403
+ Please report compatibility cases, bugs, and pull requests in the issue tracker.
401
404
 
402
405
  ---
403
406
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  [简体中文](README.zh-CN.md) · [Configuration](docs/configuration.md) · [Troubleshooting](docs/troubleshooting.md)
4
4
 
5
- > **ShellCheck tells you if your script looks portable. OpsScript Gate checks if it actually runs there.**
5
+ > ShellCheck analyzes shell syntax. OpsScript Gate executes scripts in Linux containers.
6
6
 
7
7
  [![CI](https://github.com/Mresyzz/opsscript-gate/actions/workflows/test.yml/badge.svg)](https://github.com/Mresyzz/opsscript-gate/actions/workflows/test.yml)
8
8
  [![Demo](https://github.com/Mresyzz/opsscript-gate/actions/workflows/demo.yml/badge.svg)](https://github.com/Mresyzz/opsscript-gate/actions/workflows/demo.yml)
@@ -13,17 +13,17 @@
13
13
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
14
14
  [![Supported Distros](https://img.shields.io/badge/matrix-Debian%20%7C%20Ubuntu%20%7C%20Alpine-orange.svg)](#default-test-matrix)
15
15
 
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.
16
+ **OpsScript Gate** is a runtime compatibility checker for Linux shell scripts. It executes scripts inside unprivileged Debian, Ubuntu, and Alpine containers, catching environment-specific failures, missing interpreters, and package-manager assumptions that static analysis cannot detect.
17
17
 
18
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
19
 
20
20
  ---
21
21
 
22
- ## New in v0.5.0
22
+ ## Current release: v0.5.1
23
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.
24
+ The current release includes a project config file, a dry-run plan, presets, exclusions,
25
+ and saved reports. These controls were introduced in v0.5.0; projects on v0.4.1 and
26
+ earlier do not include them.
27
27
 
28
28
  ```bash
29
29
  opsscript-gate init
@@ -41,11 +41,14 @@ repository files, sibling scripts and project dependencies are **not** mounted.
41
41
  See [configuration and migration](docs/configuration.md) and
42
42
  [why a shell script works on Ubuntu but fails on Alpine](docs/troubleshooting.md).
43
43
 
44
- ## ⚡ 30-Second Quickstart
44
+ ## Quickstart
45
45
 
46
- ### In GitHub Actions (Zero Config)
46
+ ### In GitHub Actions
47
47
 
48
- Drop this minimal workflow into `.github/workflows/shell-compat.yml`:
48
+ Add this workflow to `.github/workflows/shell-compat.yml`:
49
+
50
+ You can copy the maintained example from [`examples/github-actions/workflow.yml`](examples/github-actions/workflow.yml)
51
+ and change the `script-path`, or use the minimal workflow below.
49
52
 
50
53
  ```yaml
51
54
  name: Shell Compatibility Gate
@@ -56,15 +59,16 @@ jobs:
56
59
  runs-on: ubuntu-latest
57
60
  steps:
58
61
  - uses: actions/checkout@v7
59
- - uses: Mresyzz/opsscript-gate@v0.5.0
62
+ - uses: Mresyzz/opsscript-gate@v0.5.1
60
63
  ```
61
64
 
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.
65
+ If `script-path` is omitted, the action discovers shell scripts in the repository.
66
+ Use `opsscript-gate run --dry-run` to inspect the selection before running it.
63
67
 
64
68
  Or test a specific script with custom execution modes:
65
69
 
66
70
  ```yaml
67
- - uses: Mresyzz/opsscript-gate@v0.5.0
71
+ - uses: Mresyzz/opsscript-gate@v0.5.1
68
72
  with:
69
73
  script-path: scripts/install.sh
70
74
  shell: auto
@@ -86,7 +90,7 @@ opsscript-gate run ./scripts/install.sh
86
90
  opsscript-gate run
87
91
  ```
88
92
 
89
- ### Try a Real Failure in 30 Seconds
93
+ ### Example: package-manager mismatch
90
94
 
91
95
  Create `install.sh`:
92
96
 
@@ -113,23 +117,23 @@ Alpine 3.20 FAIL
113
117
  apt-get: not found
114
118
  ```
115
119
 
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.
120
+ The script is valid shell, but it assumes a command that is not present in Alpine.
117
121
 
118
122
  ---
119
123
 
120
- ## 🎯 Reviewer-First Experience: What It Produces
124
+ ## CI output
121
125
 
122
- Every run of OpsScript Gate generates clean, actionable feedback right where developers and reviewers need it:
126
+ The action reports failures in two places:
123
127
 
124
- ### 1. Line-Level GitHub Annotations
125
- When a failure occurs, OpsScript Gate flags the exact script line on your Pull Request's **Files Changed** view with high-confidence diagnostics and conservative remediation hints:
128
+ ### GitHub annotations
129
+ Failures include the script path, line number, distribution, and a short diagnostic:
126
130
 
127
131
  ```text
128
132
  ::error file=scripts/setup.sh,line=4,title=OpsScript Gate: [alpine:3.20] command not found: apt-get::command not found: apt-get — Alpine normally uses apk instead of apt-get.
129
133
  ```
130
134
 
131
- ### 2. GitHub Step Summary Compatibility Card
132
- A beautifully formatted markdown summary is automatically posted to `$GITHUB_STEP_SUMMARY`:
135
+ ### Step summary
136
+ The action also writes a markdown matrix to `$GITHUB_STEP_SUMMARY`:
133
137
 
134
138
  ```markdown
135
139
  ## 🛡️ OpsScript Gate Compatibility Report
@@ -148,14 +152,14 @@ A beautifully formatted markdown summary is automatically posted to `$GITHUB_STE
148
152
  | `alpine:3.20` | ❌ FAIL | `127` | `0.19s` | ⚠️ Missing command: `apt-get` (line 4)<br>💡 *Alpine normally uses apk instead of apt-get.* |
149
153
 
150
154
  <details>
151
- <summary>📋 <b>Copyable Markdown (Click to expand & copy to PR / Issue)</b></summary>
155
+ <summary><b>Markdown result</b></summary>
152
156
  ...
153
157
  </details>
154
158
  ```
155
159
 
156
160
  ---
157
161
 
158
- ## 🔍 Example: What Static Analysis Misses
162
+ ## Example: runtime behavior that static analysis misses
159
163
 
160
164
  Consider this clean deployment script:
161
165
 
@@ -192,37 +196,38 @@ Failed Distributions - Output Snippets (last 15 lines):
192
196
  sh: line 4: apt-get: not found
193
197
  ```
194
198
 
195
- **Why it failed:** Alpine Linux is musl/BusyBox-based and uses `apk`, not `apt-get`. OpsScript Gate catches the missing command (`exit code 127`) and gives you the exact line number and conservative remediation hint before deployment.
199
+ **Why it failed:** Alpine uses `apk`, not `apt-get`. The report includes the missing command and its line number.
196
200
 
197
201
  ---
198
202
 
199
- ## 🛡️ Why OpsScript Gate?
203
+ ## Scope compared with other checks
200
204
 
201
- ### OpsScript Gate vs ShellCheck vs Custom CI Matrix
205
+ ### OpsScript Gate compared with ShellCheck and a custom CI matrix
202
206
 
203
207
  | Capability | OpsScript Gate | ShellCheck | Handwritten CI Matrix |
204
208
  | :--- | :---: | :---: | :---: |
205
209
  | **Runtime execution** | **Yes** | No (Static AST only) | Yes |
206
210
  | **Real distro environments** | **Yes (Debian, Ubuntu, Alpine)** | No | Yes |
207
211
  | **Preconfigured defaults** | **Yes** | Yes | Requires custom workflow configuration |
208
- | **Hardened container defaults** | **Built-in (`ro`, `cap_drop`, resource limits)** | N/A | User-defined |
212
+ | **Restricted container defaults** | **Built-in (`ro`, `cap_drop`, resource limits)** | N/A | User-defined |
209
213
  | **Anti-hang stdin protection** | **Built-in (`</dev/null`, noninteractive)** | No | User-defined |
210
- | **Line-Level Annotations & Hints** | **Built-in (Zero config)** | Static warnings | User-defined |
214
+ | **Line-Level Annotations & Hints** | **Built-in** | Static warnings | User-defined |
211
215
  | **Parallel Matrix Execution** | **Built-in (`--jobs`)** | N/A | Manual matrix config |
212
216
 
213
- - **ShellCheck** is indispensable for static analysis (syntax, quoting, SC warnings). OpsScript Gate complements it by testing actual execution behavior in real distributions.
214
- - **Handwritten CI Matrix** requires maintaining complex Docker configurations, volume mounts, timeout guards, and log parsers across every project. OpsScript Gate packages this into a single check.
217
+ ShellCheck checks shell syntax and common mistakes. OpsScript Gate runs the script in
218
+ real distributions. A custom CI matrix can do the same job, but each repository must
219
+ maintain its own images, mounts, timeouts, and report handling.
215
220
 
216
221
  ---
217
222
 
218
- ## 🔒 Security Boundaries & Hardened Isolation
223
+ ## Security boundaries and container isolation
219
224
 
220
225
  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
226
 
222
227
  <details>
223
228
  <summary><strong>View security boundaries and runtime hardening details</strong></summary>
224
229
 
225
- OpsScript Gate applies conservative, restricted container defaults when running scripts:
230
+ The runner uses restricted container defaults:
226
231
 
227
232
  1. **Restricted Container Defaults**:
228
233
  - Containers run with `privileged=False`.
@@ -272,7 +277,7 @@ Use it when you want to know whether a shell script actually runs across the sup
272
277
 
273
278
  ---
274
279
 
275
- ## 📦 Default Test Matrix
280
+ ## Default test matrix
276
281
 
277
282
  | Image | Distribution | Focus |
278
283
  | :--- | :--- | :--- |
@@ -285,7 +290,7 @@ Customize the matrix at any time via `--matrix` or Action input `matrix`.
285
290
 
286
291
  ---
287
292
 
288
- ## 🛠️ CLI Reference
293
+ ## CLI reference
289
294
 
290
295
  ```text
291
296
  usage: opsscript-gate run [-h] [--matrix MATRIX] [-j JOBS] [--timeout TIMEOUT]
@@ -338,9 +343,9 @@ usage: opsscript-gate run [-h] [--matrix MATRIX] [-j JOBS] [--timeout TIMEOUT]
338
343
  Explicit CLI options override project settings. Explicit script paths bypass discovery
339
344
  and its exclusions. See [complete configuration semantics](docs/configuration.md).
340
345
 
341
- ## 🌍 Real-World Usage
346
+ ## Downstream example
342
347
 
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.
348
+ [`Mresyzz/linux-dev-bootstrap`](https://github.com/Mresyzz/linux-dev-bootstrap) contains a workflow that validates `install.sh` across the default Debian, Ubuntu, and Alpine matrix.
344
349
 
345
350
  See the downstream workflow: [`.github/workflows/test.yml`](https://github.com/Mresyzz/linux-dev-bootstrap/blob/main/.github/workflows/test.yml).
346
351
 
@@ -365,11 +370,9 @@ pytest -v
365
370
 
366
371
  ---
367
372
 
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.
373
+ ## Feedback
371
374
 
372
- Bug reports, real-world compatibility cases, and pull requests are especially welcome.
375
+ Please report compatibility cases, bugs, and pull requests in the issue tracker.
373
376
 
374
377
  ---
375
378
 
@@ -8,9 +8,9 @@ Bash 依赖、包管理器假设和交互式阻塞。它与 ShellCheck 互补。
8
8
  适合独立安装脚本、容器入口脚本和发布脚本。每次只挂载目标脚本,
9
9
  不挂载整个仓库;依赖其他仓库文件的脚本需要现有项目测试配合。
10
10
 
11
- ## 试用本次 v0.5.0 更新
11
+ ## 当前版本 v0.5.1
12
12
 
13
- v0.5.0 已作为正式版本发布;如果你要从源码验证当前分支,也可以执行:
13
+ v0.5.1 已作为正式版本发布;如果你要从源码验证当前分支,也可以执行:
14
14
 
15
15
  ```bash
16
16
  pip install -e .
@@ -25,7 +25,7 @@ opsscript-gate run --dry-run
25
25
  opsscript-gate run --format json --output reports/compatibility.json
26
26
  ```
27
27
 
28
- `init` 生成配置和 GitHub 工作流,不覆盖已有文件。工作流引用 `v0.5.0`。
28
+ `init` 生成配置和 GitHub 工作流,不覆盖已有文件。工作流引用 `v0.5.1`。
29
29
  预览不需要 Docker;真实运行需要 Python 3.10+
30
30
  和可访问的 Linux Docker 引擎。
31
31
 
@@ -1,5 +1,5 @@
1
1
  name: 'OpsScript Gate'
2
- description: 'Run Linux shell scripts across Debian, Ubuntu and Alpine before release to catch runtime compatibility failures'
2
+ description: 'Run Linux shell scripts in Debian, Ubuntu and Alpine containers to catch runtime compatibility failures before merge'
3
3
  author: 'Mresyzz'
4
4
  branding:
5
5
  icon: 'shield'
@@ -11,7 +11,7 @@ opsscript-gate run --dry-run
11
11
  ```
12
12
 
13
13
  `init` refuses to overwrite either `.opsscript-gate.json` or
14
- `.github/workflows/opsscript-gate.yml`. The generated workflow uses the v0.5.0 tag.
14
+ `.github/workflows/opsscript-gate.yml`. The generated workflow uses the v0.5.1 tag.
15
15
 
16
16
  The generated config uses the two-image `minimal` preset, honors recognized shell
17
17
  shebangs, disables networking, and excludes `tests/*` and `examples/*`. Review these
@@ -90,7 +90,7 @@ The Action accepts matching `config`, `preset`, `max-scripts`, `dry-run`, and `o
90
90
  inputs; `exclude` is a multiline list. Omitted execution inputs preserve config values.
91
91
 
92
92
  ```yaml
93
- - uses: Mresyzz/opsscript-gate@v0.5.0
93
+ - uses: Mresyzz/opsscript-gate@v0.5.1
94
94
  with:
95
95
  config: .opsscript-gate.json
96
96
  format: json
@@ -1,6 +1,7 @@
1
1
  # Alpine Incompatibility Example
2
2
 
3
- This example illustrates a common production issue: an ops script implicitly relies on `apt-get`, which works seamlessly on Debian and Ubuntu, but fails instantly on Alpine (which uses `apk`).
3
+ This example shows a common portability issue: a script calls `apt-get`, which is
4
+ available on Debian and Ubuntu but not on Alpine, where the package manager is `apk`.
4
5
 
5
6
  Static linters like ShellCheck usually cannot determine if `apt-get` exists on the target runtime system, but OpsScript Gate catches this immediately.
6
7
 
@@ -1,5 +1,5 @@
1
1
  # GitHub Actions Integration Example
2
2
 
3
- This example demonstrates how to integrate OpsScript Gate into a production pull request pipeline.
3
+ This example shows a GitHub Actions workflow for checking a shell script on pull requests.
4
4
 
5
- Copy `workflow.yml` into your repository at `.github/workflows/script-gate.yml` and point `script-path` to the script you want to validate.
5
+ Copy `workflow.yml` into your repository at `.github/workflows/script-gate.yml` and point `script-path` to the script you want to validate. The example uses the current v0.5.1 Action release.
@@ -15,9 +15,11 @@ jobs:
15
15
  steps:
16
16
  - name: Checkout repository
17
17
  uses: actions/checkout@v7
18
+ with:
19
+ persist-credentials: false
18
20
 
19
21
  - name: Run OpsScript Gate
20
- uses: Mresyzz/opsscript-gate@v0.3.0
22
+ uses: Mresyzz/opsscript-gate@v0.5.1
21
23
  with:
22
24
  script-path: 'scripts/deploy.sh'
23
25
  shell: auto
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "opsscript-gate"
7
- version = "0.5.0"
8
- description = "Run Linux shell scripts across Debian, Ubuntu and Alpine before release to catch runtime compatibility failures"
7
+ version = "0.5.1"
8
+ description = "Run Linux shell scripts in Debian, Ubuntu and Alpine containers to catch runtime compatibility failures before merge"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
11
11
  requires-python = ">=3.10"
@@ -1,4 +1,4 @@
1
1
  """OpsScript Gate: Lightweight cross-distro compatibility pre-check for Linux ops scripts."""
2
2
 
3
- __version__ = "0.5.0"
3
+ __version__ = "0.5.1"
4
4
  __all__ = ["__version__"]
@@ -63,7 +63,7 @@ jobs:
63
63
  - uses: actions/checkout@v7
64
64
  with:
65
65
  persist-credentials: false
66
- - uses: Mresyzz/opsscript-gate@v0.5.0
66
+ - uses: Mresyzz/opsscript-gate@v0.5.1
67
67
  with:
68
68
  config: .opsscript-gate.json
69
69
  """,
@@ -1,59 +0,0 @@
1
- # Awesome Lists 提交收录指南与 PR 模板
2
-
3
- 提交至权威的 GitHub Awesome Lists 可以带来长尾的高权重自然流量和反向链接。以下为推荐提交的目标仓库与对应的建议提交内容。
4
-
5
- ---
6
-
7
- ## 1. 目标仓库一: `sdras/awesome-actions`
8
- - **仓库地址**: https://github.com/sdras/awesome-actions
9
- - **推荐分类小节**: `Continuous Integration` 或 `Utility`
10
- - **PR 标题**:
11
- ```text
12
- Add OpsScript Gate to Continuous Integration section
13
- ```
14
- - **添加的 Markdown 行**:
15
- ```markdown
16
- - [OpsScript Gate](https://github.com/Mresyzz/opsscript-gate) - Run shell scripts across Debian, Ubuntu, and Alpine containers to catch runtime compatibility failures.
17
- ```
18
-
19
- ---
20
-
21
- ## 2. 目标仓库二: `alebcay/awesome-shell`
22
- - **仓库地址**: https://github.com/alebcay/awesome-shell
23
- - **推荐分类小节**: `Testing` 或 `Continuous Integration`
24
- - **PR 标题**:
25
- ```text
26
- Add opsscript-gate to Testing section
27
- ```
28
- - **添加的 Markdown 行**:
29
- ```markdown
30
- - [OpsScript Gate](https://github.com/Mresyzz/opsscript-gate) - Drop-in runtime compatibility gate for Linux shell scripts across multiple container distributions.
31
- ```
32
-
33
- ---
34
-
35
- ## 3. 目标仓库三: `uhub/awesome-devops`
36
- - **仓库地址**: https://github.com/uhub/awesome-devops
37
- - **推荐分类小节**: `CI/CD & Automation Tools`
38
- - **PR 标题**:
39
- ```text
40
- Add OpsScript Gate to CI/CD tools
41
- ```
42
- - **添加的 Markdown 行**:
43
- ```markdown
44
- - [OpsScript Gate](https://github.com/Mresyzz/opsscript-gate) - Lightweight multi-distro container gate verifying shell script portability before release.
45
- ```
46
-
47
- ---
48
-
49
- ## 提交 PR 时的通用描述模板 (PR Body)
50
-
51
- ```markdown
52
- ### Summary
53
-
54
- This PR adds [OpsScript Gate](https://github.com/Mresyzz/opsscript-gate) to the list.
55
-
56
- - **What it does**: A lightweight GitHub Action & CLI that runs shell scripts across Debian, Ubuntu, and Alpine Docker containers to catch runtime compatibility failures (missing package managers, unhandled interactive prompts, missing interpreters) that static analysis cannot catch.
57
- - **License**: MIT
58
- - **Format compliance**: Formatted alphabetically and according to the repository contribution guidelines.
59
- ```
@@ -1,144 +0,0 @@
1
- # 深度技术文章模版:掘金 / 知乎 / 微信公众号
2
-
3
- ## 标题推荐
4
- - 主标题: **为什么 ShellCheck 检查全绿的 Shell 脚本,在 Alpine 容器里依然会挂?**
5
- - 副标题: **浅谈 Linux 跨发行版脚本兼容性陷阱与自动化容器门禁实践**
6
-
7
- ---
8
-
9
- ## 正文大纲与精炼稿
10
-
11
- 在云原生与容器化时代,许多开源项目或企业内部运维平台都会提供类似这样的一键部署脚本:
12
-
13
- ```bash
14
- curl -fsSL https://example.com/install.sh | sh
15
- ```
16
-
17
- 为了保证脚本质量,大多数团队会在 CI(持续集成)流水线中引入业界公认的静态检查神级工具——**ShellCheck**。
18
-
19
- 当 CI 中 ShellCheck 的绿色勾勾亮起,提示 `No issues detected` 时,大家通常都会长舒一口气,以为这个脚本可以在所有 Linux 机器上高枕无忧地运行了。
20
-
21
- 然而,一旦有用户在 Alpine 容器、Debian-slim 精简镜像、或者嵌入式系统上执行该脚本时,事故往往随之而来。
22
-
23
- ---
24
-
25
- ### 一、ShellCheck 的能力边界:静态 AST 与真实运行时的鸿沟
26
-
27
- ShellCheck 极其优秀,它能够通过静态语法分析(AST Parsing)帮我们捕获绝大多数常见的语法雷区:
28
- 1. 变量未用双引号包裹导致的单词拆分与 Glob 扩展(`SC2086`);
29
- 2. 误用单括号 `[` 与双括号 `[[` 的 Bashism 语法(`SC3010`);
30
- 3. 管道子 Shell 中的变量丢失(`SC2031`)。
31
-
32
- **但是,ShellCheck 无法感知运行时环境(Runtime Environment)!**
33
-
34
- 看下面这段极其常见的安装脚本片段:
35
-
36
- ```bash
37
- #!/bin/sh
38
- set -e
39
-
40
- echo "正在更新软件包列表..."
41
- apt-get update -y
42
- apt-get install -y curl jq
43
- ```
44
-
45
- 如果你运行 `shellcheck script.sh`,报告必定是 **0 warnings, 0 errors**。
46
- 因为从 POSIX Shell 的语法层面来看,这段代码无可挑剔:
47
- - 声明了标准的 `#!/bin/sh`;
48
- - 使用了规范的变量与传参;
49
- - 没有使用任何未定义的保留语法。
50
-
51
- **但只要把它扔进 Alpine Linux 3.20 容器中执行,它会当场暴毙:**
52
-
53
- ```text
54
- /tmp/target_script.sh: line 4: apt-get: not found
55
- ```
56
-
57
- 因为 Alpine Linux 采用的是轻量级的 `musl libc` 与 `BusyBox`,其内置的包管理器是 `apk`,根本没有 `apt-get`!
58
-
59
- 除了包管理器缺失,还有更多静态检查无法覆盖的经典痛点:
60
- 1. **解释器路径不存在**:脚本第一行写了 `#!/bin/bash`,但最小化镜像里只预装了 `/bin/sh`;
61
- 2. **交互式挂死(Interactive Hang)**:脚本中某处调用命令忘记加 `-y`,或者第三方工具弹出了 `Press [Enter] to continue`,导致 CI 进程永久阻塞,直到流水线超时强杀;
62
- 3. **CRLF 换行符假性失败**:Windows 贡献者提交的脚本携带 `\r\n`,在 Linux 容器中执行时莫名其妙报错 `\r: command not found`。
63
-
64
- ---
65
-
66
- ### 二、传统多系统验证方式的繁重成本
67
-
68
- 为了规避上述问题,许多成熟开源项目开始在 GitHub Actions 中搭建手写的 Docker Matrix:
69
-
70
- ```yaml
71
- # 传统手写 Matrix 的配置开销
72
- strategy:
73
- matrix:
74
- distro: ['debian:12-slim', 'ubuntu:22.04', 'ubuntu:24.04', 'alpine:3.20']
75
- ```
76
-
77
- 但这样很快会遇到新的工程痛点:
78
- - 每个项目都要复制粘贴 50~80 行复杂的 Docker 挂载、权限配置、超时脚本;
79
- - 容器安全权限难以管控(一不小心挂载了敏感目录或赋予了特权);
80
- - 当某个发行版挂掉时,开发者必须在几千行 Actions 日志中费力翻找到底哪一行报了 127。
81
-
82
- ---
83
-
84
- ### 三、破局方案:轻量级跨发行版执行门禁 OpsScript Gate
85
-
86
- 为了彻底解决“静态检查全绿但运行时依然挂”的痛点,同时免去手写复杂 Docker 流水线的负担,我们开源了轻量级门禁工具:**OpsScript Gate**。
87
-
88
- > **项目定位:**
89
- > *"ShellCheck tells you if your script looks portable.
90
- > OpsScript Gate checks if it actually runs there."*
91
- > (ShellCheck 检查脚本看起来是否便携,OpsScript Gate 验证它在真实环境到底能不能跑通。)
92
-
93
- #### 核心设计考量
94
-
95
- 1. **多发行版开箱即跑**:
96
- 默认同时编排运行在主流基底上:
97
- - `debian:12-slim`(Glibc + APT 最小基底)
98
- - `ubuntu:22.04` / `ubuntu:24.04`(主流企业 LTS 基底)
99
- - `alpine:3.20`(Musl + BusyBox + APK 极简基底)
100
-
101
- 2. **绝对无特权沙箱与防挂死机制**:
102
- - 待测脚本强制以只读方式挂载(`:ro`);
103
- - 彻底丢弃全部内核权限(`cap_drop=["ALL"]`),禁止提权(`no-new-privileges`);
104
- - 标准输入重定向至 `</dev/null` 并注入 `DEBIAN_FRONTEND=noninteractive`。任何尝试等待用户输入的交互式命令立刻报错退出,杜绝 CI 挂死。
105
-
106
- 3. **双重置信度的缺失命令智能诊断(v0.3.0)**:
107
- 结合退出码 127 与严格的 Shell 来源报错正则特征,直接在输出表格和 GitHub Actions Step Summary 中指出具体原因:
108
- ```text
109
- +--------------------+----------+-----------+-------------------------------------+
110
- | Distro | Status | Exit Code | Details |
111
- +--------------------+----------+-----------+-------------------------------------+
112
- | debian:12-slim | PASS | 0 | OK |
113
- | ubuntu:22.04 | PASS | 0 | OK |
114
- | ubuntu:24.04 | PASS | 0 | OK |
115
- | alpine:3.20 | FAIL | 127 | command not found: apt-get |
116
- +--------------------+----------+-----------+-------------------------------------+
117
- ```
118
-
119
- 4. **极简接入,不到 10 秒**:
120
- 本地 CLI:
121
- ```bash
122
- pip install opsscript-gate
123
- opsscript-gate run ./scripts/deploy.sh
124
- ```
125
- GitHub Actions 中仅需 5 行:
126
- ```yaml
127
- - uses: actions/checkout@v7
128
- - uses: Mresyzz/opsscript-gate@v0.3.0
129
- with:
130
- script-path: scripts/deploy.sh
131
- shell: auto
132
- ```
133
-
134
- ---
135
-
136
- ### 四、总结
137
-
138
- 静态检查与动态预演不是非此即彼的对立关系,而是**相互补充的纵深防御体系**:
139
- - 用 **ShellCheck** 守住代码规范、语法健全与变量转义的第一道防线;
140
- - 用 **OpsScript Gate** 守住真实多发行版容器环境、依赖命令与解释器可执行性的最终红线。
141
-
142
- - 项目地址:https://github.com/Mresyzz/opsscript-gate
143
- - PyPI:https://pypi.org/project/opsscript-gate/
144
- - 欢迎大家 Star 关注与交流讨论!
@@ -1,56 +0,0 @@
1
- # Reddit 社区推广指南与发帖模版
2
-
3
- ## 1. 目标 Subreddit
4
- 1. **r/devops** (重点,讨论 CI/CD 与自动化脚本质量)
5
- 2. **r/bash** (讨论 Shell 脚本编写与跨平台兼容)
6
- 3. **r/linuxadmin** (系统管理与运维实践)
7
- 4. **r/githubactions** (GitHub Actions 用户群体)
8
-
9
- ---
10
-
11
- ## 2. 标题 (Title 备选)
12
- - **选项 A (痛点直击型)**:
13
- `ShellCheck said our script was portable. Alpine killed it instantly. So I built a 0-config container matrix gate.`
14
- - **选项 B (工具分享型)**:
15
- `OpsScript Gate – A lightweight GitHub Action & CLI to test shell scripts across Debian, Ubuntu, and Alpine containers`
16
-
17
- ---
18
-
19
- ## 3. 正文模版 (Post Body)
20
-
21
- ```markdown
22
- Hi r/devops!
23
-
24
- Like many of you, I maintain bootstrap scripts (`install.sh`, `setup.sh`) that users run across various Linux distros.
25
-
26
- For a long time, we relied solely on **ShellCheck** in our CI pipeline. ShellCheck is fantastic for finding unquoted variables and syntax bugs, but it consistently missed environment-specific runtime issues:
27
- - Calling `apt-get` on Alpine Linux (which uses `apk`)
28
- - Requiring `/bin/bash` when the base container only has `/bin/sh`
29
- - A subtle subshell or flag behavior differences between Debian `dash` and Alpine `ash`
30
- - Scripts freezing the CI runner indefinitely on an unexpected `read -p` prompt
31
-
32
- Running full multi-machine matrices or writing custom Docker boilerplate for every repo became tedious, so I created **OpsScript Gate**:
33
-
34
- 🔗 GitHub: https://github.com/Mresyzz/opsscript-gate
35
- 📦 PyPI: `pip install opsscript-gate`
36
-
37
- ### What it does:
38
- 1. **Multi-Distro Execution**: Runs your script across `debian:12-slim`, `ubuntu:22.04`, `ubuntu:24.04`, and `alpine:3.20` in parallel or series.
39
- 2. **Strict Security Defaults**: Unprivileged containers, all Linux capabilities dropped (`CAP_DROP=ALL`), read-only mount, and `no-new-privileges`.
40
- 3. **Deadlock & Hang Prevention**: Automatically redirects stdin from `/dev/null` and enforces non-interactive environment variables, killing interactive prompts immediately.
41
- 4. **Actionable Diagnostics**: Uses dual-signal detection (exit 127 + shell pattern matching) to identify missing commands directly in the summary table without requiring users to parse hundreds of lines of logs.
42
- 5. **Drop-in CI Integration**: 5 lines of YAML in GitHub Actions:
43
-
44
- ```yaml
45
- - uses: actions/checkout@v7
46
- - uses: Mresyzz/opsscript-gate@v0.3.0
47
- with:
48
- script-path: scripts/install.sh
49
- shell: auto
50
- ```
51
-
52
- Project positioning:
53
- > *"ShellCheck tells you if your script looks portable. OpsScript Gate checks if it actually runs there."*
54
-
55
- Feedback, issues, and contributions are very welcome! How do you currently guard against distro runtime breakages in your deployment scripts?
56
- ```
@@ -1,67 +0,0 @@
1
- # Hacker News Show HN 推广指南与发帖模版
2
-
3
- ## 1. 发布建议
4
- - **目标板块**: [Hacker News Show HN](https://news.ycombinator.com/showhn.html)
5
- - **黄金发布时间**:
6
- - 北京时间: **周二至周四 晚上 21:00 ~ 23:00**
7
- - 对应美西时间: 早上 06:00 ~ 08:00(美东 09:00 ~ 11:00,此时 HN 流量高峰开启)
8
- - **发帖建议**:
9
- - 不要带营销色彩,HN 极客崇尚第一性原理、代码开源、架构简单、无废话。
10
- - 发布后在首楼积极、专业地回复评论。
11
-
12
- ---
13
-
14
- ## 2. 标题 (Title)
15
- ```text
16
- Show HN: OpsScript Gate – ShellCheck passes, but will your script run on Alpine?
17
- ```
18
- *(备选标题: Show HN: OpsScript Gate – Zero-config container matrix gate for shell scripts)*
19
-
20
- ---
21
-
22
- ## 3. 正文 (URL / Text)
23
- *如果是发 Text Post (推荐),直接粘贴以下内容;如果发 Link Post,将 URL 设为 `https://github.com/Mresyzz/opsscript-gate` 并在首楼回复以下内容:*
24
-
25
- ```markdown
26
- Hey HN,
27
-
28
- We've all seen this happen: a team member writes an install.sh or bootstrap script, runs ShellCheck (0 warnings, valid POSIX syntax), and ships it.
29
-
30
- Then an issue arrives:
31
- "Script failed on Alpine Linux: line 4: apt-get: not found"
32
- (or "bash: not found", or a subtle dash vs ash incompatibility).
33
-
34
- Static analysis (like ShellCheck) is indispensable for quoting, AST validity, and detecting bashisms. But it cannot know whether the utilities, package managers, interpreters, or flags your script relies on actually exist inside minimal runtime environments.
35
-
36
- I built OpsScript Gate to solve this: https://github.com/Mresyzz/opsscript-gate
37
-
38
- What it does:
39
- - Runs your target script in isolated, unprivileged Docker containers across Debian 12, Ubuntu 22.04, Ubuntu 24.04, and Alpine 3.20.
40
- - Hardened container defaults: CAP_DROP=ALL, no-new-privileges, read-only script mounts, and stdin redirected from /dev/null so scripts can't deadlock CI on unhandled interactive prompts.
41
- - Extracts high-confidence failure diagnostics (e.g. requires exit code 127 + shell 'not found' pattern) to tell you exactly which command was missing without digging through logs.
42
- - Can be used locally via CLI (`pip install opsscript-gate`) or dropped into GitHub Actions in 5 lines of YAML:
43
-
44
- ```yaml
45
- - uses: actions/checkout@v7
46
- - uses: Mresyzz/opsscript-gate@v0.3.0
47
- with:
48
- script-path: scripts/setup.sh
49
- shell: auto
50
- ```
51
-
52
- The positioning:
53
- "ShellCheck tells you if your script looks portable.
54
- OpsScript Gate checks if it actually runs there."
55
-
56
- It is fully open source (MIT). I'd love to hear your thoughts, feedback, and how you currently validate multi-distro shell scripts in CI!
57
- ```
58
-
59
- ---
60
-
61
- ## 4. 常见 HN 评论预设答疑 (Q&A Cheatsheet)
62
-
63
- - **Q: Why not just use Docker matrix in GitHub Actions directly?**
64
- - **A**: Writing a custom matrix for every repository requires copying 50+ lines of workflow YAML, managing volume mounts, handling CRLF carriage returns from Windows contributors, setting timeouts, and parsing logs when a distro fails. OpsScript Gate packages all of this into a single self-contained action/CLI step with unified summary reporting.
65
-
66
- - **Q: Is it safe to run untrusted PR scripts?**
67
- - **A**: OpsScript Gate runs containers with `privileged=False`, drops all Linux capabilities (`CAP_DROP=ALL`), sets `no-new-privileges:true`, and mounts the script as read-only (`:ro`). While Docker containerization is not a complete hypervisor sandbox, these defaults strictly limit attack surface during CI evaluation.
@@ -1,68 +0,0 @@
1
- # V2EX 中文技术社区发帖模版
2
-
3
- ## 1. 发布建议
4
- - **节点**: [分享创造](https://v2ex.com/go/create) 或 [程序员](https://v2ex.com/go/programmer)
5
- - **时间**: 工作日上午 10:30 ~ 11:30 或 下午 14:30 ~ 15:30(摸鱼高峰期流量最大)
6
-
7
- ---
8
-
9
- ## 2. 标题 (Title)
10
- ```text
11
- 为什么 ShellCheck 检查全绿的运维脚本,在 Alpine 容器里依然会挂?做了一个开源轻量预演门禁 OpsScript Gate
12
- ```
13
-
14
- ---
15
-
16
- ## 3. 正文模版 (Markdown)
17
-
18
- 相信很多做运维、后端或写开源工具的朋友都写过类似 `curl -fsSL https://.../install.sh | bash` 的安装脚本。
19
-
20
- 我们在本地 Ubuntu / Mac 下自测通过,CI 里配置了著名的静态检查工具 **ShellCheck**,代码一查全绿,0 warnings、0 errors。满心欢喜发版后,很快就会收到 Issue:
21
- - “在 Alpine Linux 下执行报错:`line 4: apt-get: not found`”
22
- - “精简容器镜像里没有 `/bin/bash`,只有 BusyBox `/bin/sh`,脚本直接退出”
23
- - “脚本里的某个命令突然在交互式等待输入,CI 流水线直接卡死直到超时”
24
-
25
- ### 为什么 ShellCheck 检查全绿依然会挂?
26
-
27
- 因为 ShellCheck 是**纯静态 AST 分析器**。它能帮你揪出未加引号的变量扩展、语法错误或 Bashism,但它**无法感知你的脚本运行在什么具体的系统基底上**。
28
- - 语法是标准的 POSIX,但调用的外部命令在 Alpine/Debian-slim 镜像里根本不存在;
29
- - 脚本里的 Shebang 声明了 `#!/bin/bash`,但目标系统只有 `dash` 或 `ash`。
30
-
31
- 为此我做了一个轻量级、开箱即用的跨发行版自动化执行门禁:**OpsScript Gate**。
32
-
33
- - 🔗 **GitHub**: https://github.com/Mresyzz/opsscript-gate
34
- - 📦 **PyPI**: https://pypi.org/project/opsscript-gate/
35
- - 🏷️ **开源协议**: MIT
36
-
37
- ---
38
-
39
- ### 它能做什么?
40
-
41
- 1. **多发行版矩阵预演**:
42
- 默认在标准的 `debian:12-slim`、`ubuntu:22.04`、`ubuntu:24.04` 和 `alpine:3.20` 纯净容器中同时预演运行你的脚本。
43
- 2. **严密的无特权安全沙箱**:
44
- 强制无特权(`privileged=False`)、剥离全部内核 Capabilities(`CAP_DROP=ALL`)、脚本文件只读挂载(`:ro`)、禁止提权(`no-new-privileges`)。
45
- 3. **输入挂起断流防御**:
46
- 强制断开标准输入(`</dev/null`)并注入非交互环境变量。一旦脚本试图弹出交互式确认(如未加 `-y` 的安装命令),立即报错退出,绝不会挂死 CI。
47
- 4. **缺失命令精准提取(v0.3.0 新特性)**:
48
- 自动识别退出码 127 与 Shell 报错特征,直接在终端表格与 GitHub Step Summary 中展示 `missing command: apt-get`,不用翻查海量执行日志。
49
- 5. **极简接入**:
50
- 本地一行即可运行:
51
- ```bash
52
- pip install opsscript-gate
53
- opsscript-gate run ./scripts/setup.sh
54
- ```
55
- GitHub Actions 中只需 5 行代码:
56
- ```yaml
57
- - uses: actions/checkout@v7
58
- - uses: Mresyzz/opsscript-gate@v0.3.0
59
- with:
60
- script-path: scripts/setup.sh
61
- shell: auto
62
- ```
63
-
64
- 项目的核心定位是:
65
- > **“ShellCheck tells you if your script looks portable. OpsScript Gate checks if it actually runs there.”**
66
- > (ShellCheck 验证脚本语法看起来是否便携,OpsScript Gate 验证它在真实环境里到底能不能跑起来。)
67
-
68
- 欢迎大家 Star、试用或提意见,也期待与大家交流日常编写跨系统 Shell 脚本时的避坑经验!
File without changes