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.
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/CHANGELOG.md +21 -18
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/PKG-INFO +44 -41
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/README.md +42 -39
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/README.zh-CN.md +3 -3
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/action.yml +1 -1
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/docs/configuration.md +2 -2
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/alpine-incompatibility/README.md +2 -1
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/github-actions/README.md +2 -2
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/github-actions/workflow.yml +3 -1
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/pyproject.toml +2 -2
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/__init__.py +1 -1
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/config.py +1 -1
- opsscript_gate-0.5.0/docs/promotion/awesome-lists-pr.md +0 -59
- opsscript_gate-0.5.0/docs/promotion/juejin-zhihu-article.md +0 -144
- opsscript_gate-0.5.0/docs/promotion/reddit-devops.md +0 -56
- opsscript_gate-0.5.0/docs/promotion/show-hn.md +0 -67
- opsscript_gate-0.5.0/docs/promotion/v2ex-post.md +0 -68
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.gitattributes +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/ISSUE_TEMPLATE/distro_request.yml +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/assets/social-preview.png +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/pull_request_template.md +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/workflows/demo.yml +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/workflows/release.yml +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.github/workflows/test.yml +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/.gitignore +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/CONTRIBUTING.md +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/LICENSE +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/ROADMAP.md +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/SECURITY.md +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/docs/troubleshooting.md +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/alpine-incompatibility/apt_dependency.sh +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/basic/README.md +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/basic/clean_setup.sh +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/interactive-hang/README.md +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/interactive-hang/prompt_confirm.sh +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/cli.py +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/discovery.py +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/models.py +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/remediation.py +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/reporter.py +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/src/opsscript_gate/runner.py +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/tests/fixtures/fail_deps.sh +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/tests/fixtures/fail_hang.sh +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/tests/fixtures/pass_basic.sh +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/tests/test_action_contract.py +0 -0
- {opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/tests/test_onboarding.py +0 -0
- {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
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
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
|
|
16
|
-
- Discovery skips symlinks and non-files and
|
|
17
|
-
- CLI
|
|
18
|
-
-
|
|
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
|
-
-
|
|
35
|
-
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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
|
|
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.
|
|
4
|
-
Summary: Run Linux shell scripts
|
|
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
|
-
>
|
|
33
|
+
> ShellCheck analyzes shell syntax. OpsScript Gate executes scripts in Linux containers.
|
|
34
34
|
|
|
35
35
|
[](https://github.com/Mresyzz/opsscript-gate/actions/workflows/test.yml)
|
|
36
36
|
[](https://github.com/Mresyzz/opsscript-gate/actions/workflows/demo.yml)
|
|
@@ -41,17 +41,17 @@ Description-Content-Type: text/markdown
|
|
|
41
41
|
[](https://opensource.org/licenses/MIT)
|
|
42
42
|
[](#default-test-matrix)
|
|
43
43
|
|
|
44
|
-
**OpsScript Gate** is a
|
|
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
|
-
##
|
|
50
|
+
## Current release: v0.5.1
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
##
|
|
72
|
+
## Quickstart
|
|
73
73
|
|
|
74
|
-
### In GitHub Actions
|
|
74
|
+
### In GitHub Actions
|
|
75
75
|
|
|
76
|
-
|
|
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.
|
|
90
|
+
- uses: Mresyzz/opsscript-gate@v0.5.1
|
|
88
91
|
```
|
|
89
92
|
|
|
90
|
-
|
|
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.
|
|
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
|
-
###
|
|
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
|
|
148
|
+
The script is valid shell, but it assumes a command that is not present in Alpine.
|
|
145
149
|
|
|
146
150
|
---
|
|
147
151
|
|
|
148
|
-
##
|
|
152
|
+
## CI output
|
|
149
153
|
|
|
150
|
-
|
|
154
|
+
The action reports failures in two places:
|
|
151
155
|
|
|
152
|
-
###
|
|
153
|
-
|
|
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
|
-
###
|
|
160
|
-
|
|
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
|
|
183
|
+
<summary><b>Markdown result</b></summary>
|
|
180
184
|
...
|
|
181
185
|
</details>
|
|
182
186
|
```
|
|
183
187
|
|
|
184
188
|
---
|
|
185
189
|
|
|
186
|
-
##
|
|
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
|
|
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
|
-
##
|
|
231
|
+
## Scope compared with other checks
|
|
228
232
|
|
|
229
|
-
### OpsScript Gate
|
|
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
|
-
| **
|
|
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
|
|
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
|
-
|
|
242
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
374
|
+
## Downstream example
|
|
370
375
|
|
|
371
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
>
|
|
5
|
+
> ShellCheck analyzes shell syntax. OpsScript Gate executes scripts in Linux containers.
|
|
6
6
|
|
|
7
7
|
[](https://github.com/Mresyzz/opsscript-gate/actions/workflows/test.yml)
|
|
8
8
|
[](https://github.com/Mresyzz/opsscript-gate/actions/workflows/demo.yml)
|
|
@@ -13,17 +13,17 @@
|
|
|
13
13
|
[](https://opensource.org/licenses/MIT)
|
|
14
14
|
[](#default-test-matrix)
|
|
15
15
|
|
|
16
|
-
**OpsScript Gate** is a
|
|
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
|
-
##
|
|
22
|
+
## Current release: v0.5.1
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
##
|
|
44
|
+
## Quickstart
|
|
45
45
|
|
|
46
|
-
### In GitHub Actions
|
|
46
|
+
### In GitHub Actions
|
|
47
47
|
|
|
48
|
-
|
|
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.
|
|
62
|
+
- uses: Mresyzz/opsscript-gate@v0.5.1
|
|
60
63
|
```
|
|
61
64
|
|
|
62
|
-
|
|
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.
|
|
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
|
-
###
|
|
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
|
|
120
|
+
The script is valid shell, but it assumes a command that is not present in Alpine.
|
|
117
121
|
|
|
118
122
|
---
|
|
119
123
|
|
|
120
|
-
##
|
|
124
|
+
## CI output
|
|
121
125
|
|
|
122
|
-
|
|
126
|
+
The action reports failures in two places:
|
|
123
127
|
|
|
124
|
-
###
|
|
125
|
-
|
|
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
|
-
###
|
|
132
|
-
|
|
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
|
|
155
|
+
<summary><b>Markdown result</b></summary>
|
|
152
156
|
...
|
|
153
157
|
</details>
|
|
154
158
|
```
|
|
155
159
|
|
|
156
160
|
---
|
|
157
161
|
|
|
158
|
-
##
|
|
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
|
|
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
|
-
##
|
|
203
|
+
## Scope compared with other checks
|
|
200
204
|
|
|
201
|
-
### OpsScript Gate
|
|
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
|
-
| **
|
|
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
|
|
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
|
-
|
|
214
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
346
|
+
## Downstream example
|
|
342
347
|
|
|
343
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
##
|
|
11
|
+
## 当前版本 v0.5.1
|
|
12
12
|
|
|
13
|
-
v0.5.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
8
|
-
description = "Run Linux shell scripts
|
|
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,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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{opsscript_gate-0.5.0 → opsscript_gate-0.5.1}/examples/alpine-incompatibility/apt_dependency.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|