opsscript-gate 0.8.3__tar.gz → 0.9.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 (57) hide show
  1. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/.github/workflow-templates/opsscript-gate.properties.json +1 -1
  2. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/CHANGELOG.md +20 -0
  3. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/CITATION.cff +1 -1
  4. opsscript_gate-0.8.3/README.md → opsscript_gate-0.9.1/PKG-INFO +86 -9
  5. opsscript_gate-0.8.3/PKG-INFO → opsscript_gate-0.9.1/README.md +55 -39
  6. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/README.zh-CN.md +15 -6
  7. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/ROADMAP.md +9 -0
  8. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/action.yml +7 -1
  9. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/demo.html +4 -4
  10. opsscript_gate-0.9.1/docs/command-not-found.html +122 -0
  11. opsscript_gate-0.9.1/docs/command-not-found.md +119 -0
  12. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/docs/configuration.md +8 -3
  13. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/docs/gpt-oss.md +1 -1
  14. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/docs/index.html +13 -5
  15. opsscript_gate-0.9.1/docs/llms.txt +26 -0
  16. opsscript_gate-0.9.1/docs/sitemap.xml +5 -0
  17. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/examples/github-actions/README.md +1 -1
  18. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/examples/github-actions/workflow.yml +1 -1
  19. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/index.html +13 -5
  20. opsscript_gate-0.9.1/llms.txt +26 -0
  21. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/pyproject.toml +10 -2
  22. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/src/opsscript_gate/__init__.py +1 -1
  23. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/src/opsscript_gate/cli.py +59 -42
  24. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/src/opsscript_gate/config.py +9 -2
  25. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/src/opsscript_gate/discovery.py +12 -2
  26. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/src/opsscript_gate/models.py +20 -1
  27. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/src/opsscript_gate/reporter.py +16 -5
  28. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/src/opsscript_gate/runner.py +148 -34
  29. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/tests/test_action_contract.py +2 -2
  30. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/tests/test_onboarding.py +32 -1
  31. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/tests/test_runner.py +132 -1
  32. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/.gitattributes +0 -0
  33. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  34. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/.github/ISSUE_TEMPLATE/distro_request.yml +0 -0
  35. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  36. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/.github/assets/social-preview.png +0 -0
  37. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/.github/pull_request_template.md +0 -0
  38. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/.github/workflow-templates/opsscript-gate.yml +0 -0
  39. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/.github/workflows/demo.yml +0 -0
  40. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/.github/workflows/deploy-pages.yml +0 -0
  41. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/.github/workflows/release.yml +0 -0
  42. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/.github/workflows/test.yml +0 -0
  43. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/.gitignore +0 -0
  44. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/CONTRIBUTING.md +0 -0
  45. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/LICENSE +0 -0
  46. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/SECURITY.md +0 -0
  47. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/docs/troubleshooting.md +0 -0
  48. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/examples/alpine-incompatibility/README.md +0 -0
  49. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/examples/alpine-incompatibility/apt_dependency.sh +0 -0
  50. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/examples/basic/README.md +0 -0
  51. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/examples/basic/clean_setup.sh +0 -0
  52. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/examples/interactive-hang/README.md +0 -0
  53. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/examples/interactive-hang/prompt_confirm.sh +0 -0
  54. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/src/opsscript_gate/remediation.py +0 -0
  55. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/tests/fixtures/fail_deps.sh +0 -0
  56. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/tests/fixtures/fail_hang.sh +0 -0
  57. {opsscript_gate-0.8.3 → opsscript_gate-0.9.1}/tests/fixtures/pass_basic.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "OpsScript Gate shell compatibility",
3
- "description": "Run shell scripts in Debian, Ubuntu and Alpine containers on changes to shell files.",
3
+ "description": "Run shell scripts in Debian, Ubuntu and Alpine containers to catch command not found and runtime compatibility failures.",
4
4
  "iconName": "octicon-shield-check",
5
5
  "categories": ["Continuous integration", "Testing"],
6
6
  "filePatterns": [".*\\.sh$", ".*\\.bash$"]
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.1] - 2026-10-03
4
+
5
+ ### Fixed
6
+ - Normalized discovery exclusions so top-level paths match with or without `./`.
7
+ - Made SARIF output conform to the 2.1.0 result schema.
8
+ - Failed closed when Docker omitted an exit code and separated package setup errors from script failures.
9
+ - Validated project configuration in `doctor` and handled UTF-8 BOM scripts.
10
+ - Distinguished Docker wait API errors from genuine execution timeouts.
11
+
12
+ ## [0.9.0] - 2026-10-03
13
+
14
+ ### Added
15
+ - Added `--repeat` and Action/config `repeat` support to detect flaky runtime behavior per distribution.
16
+ - Added attempt counts and `FLAKY` status to JSON, Markdown, SARIF, terminal, and GitHub Step Summary reports.
17
+
18
+ ### Fixed
19
+ - Bounded project configuration reads to prevent oversized configuration files from consuming unbounded memory.
20
+ - Prefer Docker's blocking wait API over repeated synchronous container reloads, while retaining a bounded compatibility fallback.
21
+ - Removed duplicated CLI execution error handling and the unused `asdict` import.
22
+
3
23
  ## [0.8.3] - 2026-10-02
4
24
 
5
25
  - Corrected the PyPI homepage and demo links to point to the OpsScript Gate Pages site.
@@ -5,7 +5,7 @@ type: software
5
5
  authors:
6
6
  - family-names: Mresyzz
7
7
  given-names: Mresy
8
- version: 0.8.3
8
+ version: 0.9.0
9
9
  date-released: 2026-10-02
10
10
  repository-code: https://github.com/Mresyzz/opsscript-gate
11
11
  url: https://github.com/Mresyzz/opsscript-gate
@@ -1,8 +1,39 @@
1
- # OpsScript Gate — GitHub Action for cross-distro shell installer testing
1
+ Metadata-Version: 2.5
2
+ Name: opsscript-gate
3
+ Version: 0.9.1
4
+ Summary: Cross-distro shell script compatibility checker for runtime testing in Debian, Ubuntu and Alpine
5
+ Project-URL: Homepage, https://mresyzz.github.io/opsscript-gate/
6
+ Project-URL: Demo, https://mresyzz.github.io/opsscript-gate/
7
+ Project-URL: Repository, https://github.com/Mresyzz/opsscript-gate
8
+ Project-URL: Issues, https://github.com/Mresyzz/opsscript-gate/issues
9
+ Project-URL: Changelog, https://github.com/Mresyzz/opsscript-gate/blob/main/CHANGELOG.md
10
+ Project-URL: Documentation, https://github.com/Mresyzz/opsscript-gate/tree/main/docs
11
+ Project-URL: Troubleshooting, https://github.com/Mresyzz/opsscript-gate/blob/main/docs/command-not-found.md
12
+ Author: Mresyzz
13
+ License: MIT
14
+ License-File: LICENSE
15
+ Keywords: alpine,apk,apt-get,bashisms,bootstrap,busybox,ci,command-not-found,compatibility,cross-distro,debian,devops,docker,github-actions,installer,installer-testing,linux,portable-shell,posix,runtime-compatibility,shell,shell-portability,shell-script-testing,shellcheck,shellcheck-alternative,testing,ubuntu
16
+ Classifier: Development Status :: 4 - Beta
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Intended Audience :: System Administrators
19
+ Classifier: License :: OSI Approved :: MIT License
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Topic :: Software Development :: Testing
25
+ Classifier: Topic :: System :: Systems Administration
26
+ Requires-Python: >=3.10
27
+ Requires-Dist: docker>=7.0.0
28
+ Provides-Extra: test
29
+ Requires-Dist: pytest>=8.0.0; extra == 'test'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # OpsScript Gate — cross-distro shell script runtime compatibility checker
2
33
 
3
34
  [Live demo](https://mresyzz.github.io/opsscript-gate/) · [简体中文](README.zh-CN.md) · [Configuration](docs/configuration.md) · [Troubleshooting](docs/troubleshooting.md) · [Self-hosted model installer guide](docs/gpt-oss.md)
4
35
 
5
- > A GitHub Action and Python CLI that executes shell installers in Debian, Ubuntu, and Alpine containers. ShellCheck analyzes syntax; OpsScript Gate verifies runtime compatibility.
36
+ > **OpsScript Gate is a cross-distro shell script runtime compatibility checker.** The GitHub Action and Python CLI test shell installers in real Debian, Ubuntu, and Alpine containers and diagnose failures such as `command not found`. Use it alongside ShellCheck to check both source code and runtime behavior.
6
37
 
7
38
  [![CI](https://github.com/Mresyzz/opsscript-gate/actions/workflows/test.yml/badge.svg)](https://github.com/Mresyzz/opsscript-gate/actions/workflows/test.yml)
8
39
  [![Demo](https://github.com/Mresyzz/opsscript-gate/actions/workflows/demo.yml/badge.svg)](https://github.com/Mresyzz/opsscript-gate/actions/workflows/demo.yml)
@@ -13,21 +44,55 @@
13
44
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
14
45
  [![Supported Distros](https://img.shields.io/badge/matrix-Debian%20%7C%20Ubuntu%20%7C%20Alpine-orange.svg)](#default-test-matrix)
15
46
 
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.
47
+ **OpsScript Gate** is a cross-distro shell script runtime compatibility checker for Linux. It executes scripts inside unprivileged Debian, Ubuntu, and Alpine containers, catching environment-specific failures, missing interpreters, package-manager assumptions, and exit code 127 errors that static analysis cannot detect.
17
48
 
18
49
  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
50
 
51
+ If you arrived here from an error or a search, these are the problems this project answers:
52
+
53
+ - `command not found` when a shell script runs in Docker or GitHub Actions
54
+ - `apt-get: not found` on Alpine, or `apk: not found` on Debian/Ubuntu
55
+ - a shell installer that passes ShellCheck but fails at runtime
56
+ - a shell script compatibility checker that tests runtime commands, not only syntax
57
+ - cross-distro shell testing for installers, bootstrap scripts, and entrypoints
58
+ - testing one shell script across real Debian, Ubuntu, and Alpine containers
59
+ - replacing a hand-written cross-distribution CI matrix with one reusable action
60
+
61
+ Start with the problem-first guide: [debug `command not found` across Debian, Ubuntu, and Alpine](docs/command-not-found.md). It includes a failing script, the diagnosis, a copyable workflow, and the local CLI equivalent.
62
+
20
63
  If your repository contains an installer, bootstrap script, or shell entrypoint, start with the
21
64
  [interactive demo](https://mresyzz.github.io/opsscript-gate/), then copy the maintained
22
65
  [zero-configuration workflow](.github/workflow-templates/opsscript-gate.yml). The demo explains the
23
66
  ShellCheck-versus-runtime gap and the workflow is ready to review in your own repository.
24
67
 
68
+ ### The 15-second proof
69
+
70
+ This script is valid POSIX shell and can pass a syntax linter, but it fails on Alpine because
71
+ `apt-get` is not installed there:
72
+
73
+ ```sh
74
+ #!/bin/sh
75
+ set -eu
76
+ apt-get --version
77
+ ```
78
+
79
+ Run it locally with `opsscript-gate run ./install.sh`, or add the following step to CI:
80
+
81
+ ```yaml
82
+ - uses: Mresyzz/opsscript-gate@v0.9.1
83
+ with:
84
+ script-path: install.sh
85
+ ```
86
+
87
+ The result identifies the failing distribution, exit code, line, missing command, and the usual remediation (`apk` on Alpine). See the [full `command not found` guide](docs/command-not-found.md) for Bash, `curl`, `jq`, and noninteractive prompt cases.
88
+
25
89
  ---
26
90
 
27
- ## Current release: v0.8.3
91
+ ## Current release: v0.9.1
28
92
 
29
93
  The current release includes a project config file, a dry-run plan, presets, exclusions,
30
- saved reports, and changed-script selection for pull requests. The project controls
94
+ saved reports, changed-script selection for pull requests, and repeat runs for detecting
95
+ flaky runtime behavior. The project controls
31
96
  were introduced in v0.5.0; projects on v0.4.1 and earlier do not include them.
32
97
 
33
98
  ```bash
@@ -35,6 +100,8 @@ opsscript-gate init
35
100
  opsscript-gate doctor
36
101
  opsscript-gate run --dry-run
37
102
  opsscript-gate run --format json --output reports/compatibility.json
103
+ # Repeat each distro three times when diagnosing intermittent failures
104
+ opsscript-gate run ./install.sh --repeat 3
38
105
  ```
39
106
 
40
107
  `init` creates `.opsscript-gate.json` and a GitHub Actions workflow without replacing
@@ -75,7 +142,7 @@ jobs:
75
142
  - uses: actions/checkout@v7
76
143
  with:
77
144
  persist-credentials: false
78
- - uses: Mresyzz/opsscript-gate@v0.8.3
145
+ - uses: Mresyzz/opsscript-gate@v0.9.1
79
146
  ```
80
147
 
81
148
  This zero-configuration form discovers shell scripts in the repository. Use
@@ -86,11 +153,13 @@ repository has a single installer. For self-hosted model installers, see the
86
153
  Or test a specific script with custom execution modes:
87
154
 
88
155
  ```yaml
89
- - uses: Mresyzz/opsscript-gate@v0.8.3
156
+ - uses: Mresyzz/opsscript-gate@v0.9.1
90
157
  with:
91
158
  script-path: scripts/install.sh
92
159
  shell: auto
93
160
  jobs: 4
161
+ # Optional: expose intermittent runtime failures
162
+ repeat: 3
94
163
  ```
95
164
 
96
165
  ### In Local Terminal (CLI)
@@ -118,7 +187,7 @@ unrelated scripts:
118
187
  with:
119
188
  persist-credentials: false
120
189
  fetch-depth: 0
121
- - uses: Mresyzz/opsscript-gate@v0.8.3
190
+ - uses: Mresyzz/opsscript-gate@v0.9.1
122
191
  with:
123
192
  changed-since: ${{ github.event.pull_request.base.sha }}
124
193
  preset: minimal
@@ -128,6 +197,14 @@ If the change does not include a shell script, the check passes without starting
128
197
  container. The same selection can be previewed locally with
129
198
  `opsscript-gate run --changed-since origin/main --dry-run`.
130
199
 
200
+ ### Detect intermittent runtime failures
201
+
202
+ Use `--repeat N` when a script sometimes passes and sometimes fails in CI. OpsScript
203
+ Gate runs each distribution N times and reports `FLAKY` when the same distribution has
204
+ both passing and failing attempts. The JSON, Markdown, SARIF, annotations, and step
205
+ summary include the attempt counts so a retry cannot silently turn an unstable script
206
+ green.
207
+
131
208
  ### Example: package-manager mismatch
132
209
 
133
210
  Create `install.sh`:
@@ -201,7 +278,7 @@ Use `sarif` when the result should appear in GitHub Code Scanning or another
201
278
  SARIF-compatible viewer. Upload it explicitly with the official upload action:
202
279
 
203
280
  ```yaml
204
- - uses: Mresyzz/opsscript-gate@v0.8.3
281
+ - uses: Mresyzz/opsscript-gate@v0.9.1
205
282
  with:
206
283
  script-path: scripts/install.sh
207
284
  format: sarif
@@ -1,38 +1,8 @@
1
- Metadata-Version: 2.5
2
- Name: opsscript-gate
3
- Version: 0.8.3
4
- Summary: Run Linux shell scripts in Debian, Ubuntu and Alpine containers to catch runtime compatibility failures before merge
5
- Project-URL: Homepage, https://mresyzz.github.io/opsscript-gate/
6
- Project-URL: Demo, https://mresyzz.github.io/opsscript-gate/
7
- Project-URL: Repository, https://github.com/Mresyzz/opsscript-gate
8
- Project-URL: Issues, https://github.com/Mresyzz/opsscript-gate/issues
9
- Project-URL: Changelog, https://github.com/Mresyzz/opsscript-gate/blob/main/CHANGELOG.md
10
- Project-URL: Documentation, https://github.com/Mresyzz/opsscript-gate/tree/main/docs
11
- Author: Mresyzz
12
- License: MIT
13
- License-File: LICENSE
14
- Keywords: alpine,bootstrap,busybox,ci,compatibility,cross-distro,debian,devops,docker,github-actions,installer,installer-testing,linux,posix,shell,shell-portability,shellcheck,shellcheck-alternative,testing,ubuntu
15
- Classifier: Development Status :: 4 - Beta
16
- Classifier: Intended Audience :: Developers
17
- Classifier: Intended Audience :: System Administrators
18
- Classifier: License :: OSI Approved :: MIT License
19
- Classifier: Programming Language :: Python :: 3
20
- Classifier: Programming Language :: Python :: 3.10
21
- Classifier: Programming Language :: Python :: 3.11
22
- Classifier: Programming Language :: Python :: 3.12
23
- Classifier: Topic :: Software Development :: Testing
24
- Classifier: Topic :: System :: Systems Administration
25
- Requires-Python: >=3.10
26
- Requires-Dist: docker>=7.0.0
27
- Provides-Extra: test
28
- Requires-Dist: pytest>=8.0.0; extra == 'test'
29
- Description-Content-Type: text/markdown
30
-
31
- # OpsScript Gate — GitHub Action for cross-distro shell installer testing
1
+ # OpsScript Gate — cross-distro shell script runtime compatibility checker
32
2
 
33
3
  [Live demo](https://mresyzz.github.io/opsscript-gate/) · [简体中文](README.zh-CN.md) · [Configuration](docs/configuration.md) · [Troubleshooting](docs/troubleshooting.md) · [Self-hosted model installer guide](docs/gpt-oss.md)
34
4
 
35
- > A GitHub Action and Python CLI that executes shell installers in Debian, Ubuntu, and Alpine containers. ShellCheck analyzes syntax; OpsScript Gate verifies runtime compatibility.
5
+ > **OpsScript Gate is a cross-distro shell script runtime compatibility checker.** The GitHub Action and Python CLI test shell installers in real Debian, Ubuntu, and Alpine containers and diagnose failures such as `command not found`. Use it alongside ShellCheck to check both source code and runtime behavior.
36
6
 
37
7
  [![CI](https://github.com/Mresyzz/opsscript-gate/actions/workflows/test.yml/badge.svg)](https://github.com/Mresyzz/opsscript-gate/actions/workflows/test.yml)
38
8
  [![Demo](https://github.com/Mresyzz/opsscript-gate/actions/workflows/demo.yml/badge.svg)](https://github.com/Mresyzz/opsscript-gate/actions/workflows/demo.yml)
@@ -43,21 +13,55 @@ Description-Content-Type: text/markdown
43
13
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
44
14
  [![Supported Distros](https://img.shields.io/badge/matrix-Debian%20%7C%20Ubuntu%20%7C%20Alpine-orange.svg)](#default-test-matrix)
45
15
 
46
- **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.
16
+ **OpsScript Gate** is a cross-distro shell script runtime compatibility checker for Linux. It executes scripts inside unprivileged Debian, Ubuntu, and Alpine containers, catching environment-specific failures, missing interpreters, package-manager assumptions, and exit code 127 errors that static analysis cannot detect.
47
17
 
48
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.
49
19
 
20
+ If you arrived here from an error or a search, these are the problems this project answers:
21
+
22
+ - `command not found` when a shell script runs in Docker or GitHub Actions
23
+ - `apt-get: not found` on Alpine, or `apk: not found` on Debian/Ubuntu
24
+ - a shell installer that passes ShellCheck but fails at runtime
25
+ - a shell script compatibility checker that tests runtime commands, not only syntax
26
+ - cross-distro shell testing for installers, bootstrap scripts, and entrypoints
27
+ - testing one shell script across real Debian, Ubuntu, and Alpine containers
28
+ - replacing a hand-written cross-distribution CI matrix with one reusable action
29
+
30
+ Start with the problem-first guide: [debug `command not found` across Debian, Ubuntu, and Alpine](docs/command-not-found.md). It includes a failing script, the diagnosis, a copyable workflow, and the local CLI equivalent.
31
+
50
32
  If your repository contains an installer, bootstrap script, or shell entrypoint, start with the
51
33
  [interactive demo](https://mresyzz.github.io/opsscript-gate/), then copy the maintained
52
34
  [zero-configuration workflow](.github/workflow-templates/opsscript-gate.yml). The demo explains the
53
35
  ShellCheck-versus-runtime gap and the workflow is ready to review in your own repository.
54
36
 
37
+ ### The 15-second proof
38
+
39
+ This script is valid POSIX shell and can pass a syntax linter, but it fails on Alpine because
40
+ `apt-get` is not installed there:
41
+
42
+ ```sh
43
+ #!/bin/sh
44
+ set -eu
45
+ apt-get --version
46
+ ```
47
+
48
+ Run it locally with `opsscript-gate run ./install.sh`, or add the following step to CI:
49
+
50
+ ```yaml
51
+ - uses: Mresyzz/opsscript-gate@v0.9.1
52
+ with:
53
+ script-path: install.sh
54
+ ```
55
+
56
+ The result identifies the failing distribution, exit code, line, missing command, and the usual remediation (`apk` on Alpine). See the [full `command not found` guide](docs/command-not-found.md) for Bash, `curl`, `jq`, and noninteractive prompt cases.
57
+
55
58
  ---
56
59
 
57
- ## Current release: v0.8.3
60
+ ## Current release: v0.9.1
58
61
 
59
62
  The current release includes a project config file, a dry-run plan, presets, exclusions,
60
- saved reports, and changed-script selection for pull requests. The project controls
63
+ saved reports, changed-script selection for pull requests, and repeat runs for detecting
64
+ flaky runtime behavior. The project controls
61
65
  were introduced in v0.5.0; projects on v0.4.1 and earlier do not include them.
62
66
 
63
67
  ```bash
@@ -65,6 +69,8 @@ opsscript-gate init
65
69
  opsscript-gate doctor
66
70
  opsscript-gate run --dry-run
67
71
  opsscript-gate run --format json --output reports/compatibility.json
72
+ # Repeat each distro three times when diagnosing intermittent failures
73
+ opsscript-gate run ./install.sh --repeat 3
68
74
  ```
69
75
 
70
76
  `init` creates `.opsscript-gate.json` and a GitHub Actions workflow without replacing
@@ -105,7 +111,7 @@ jobs:
105
111
  - uses: actions/checkout@v7
106
112
  with:
107
113
  persist-credentials: false
108
- - uses: Mresyzz/opsscript-gate@v0.8.3
114
+ - uses: Mresyzz/opsscript-gate@v0.9.1
109
115
  ```
110
116
 
111
117
  This zero-configuration form discovers shell scripts in the repository. Use
@@ -116,11 +122,13 @@ repository has a single installer. For self-hosted model installers, see the
116
122
  Or test a specific script with custom execution modes:
117
123
 
118
124
  ```yaml
119
- - uses: Mresyzz/opsscript-gate@v0.8.3
125
+ - uses: Mresyzz/opsscript-gate@v0.9.1
120
126
  with:
121
127
  script-path: scripts/install.sh
122
128
  shell: auto
123
129
  jobs: 4
130
+ # Optional: expose intermittent runtime failures
131
+ repeat: 3
124
132
  ```
125
133
 
126
134
  ### In Local Terminal (CLI)
@@ -148,7 +156,7 @@ unrelated scripts:
148
156
  with:
149
157
  persist-credentials: false
150
158
  fetch-depth: 0
151
- - uses: Mresyzz/opsscript-gate@v0.8.3
159
+ - uses: Mresyzz/opsscript-gate@v0.9.1
152
160
  with:
153
161
  changed-since: ${{ github.event.pull_request.base.sha }}
154
162
  preset: minimal
@@ -158,6 +166,14 @@ If the change does not include a shell script, the check passes without starting
158
166
  container. The same selection can be previewed locally with
159
167
  `opsscript-gate run --changed-since origin/main --dry-run`.
160
168
 
169
+ ### Detect intermittent runtime failures
170
+
171
+ Use `--repeat N` when a script sometimes passes and sometimes fails in CI. OpsScript
172
+ Gate runs each distribution N times and reports `FLAKY` when the same distribution has
173
+ both passing and failing attempts. The JSON, Markdown, SARIF, annotations, and step
174
+ summary include the attempt counts so a retry cannot silently turn an unstable script
175
+ green.
176
+
161
177
  ### Example: package-manager mismatch
162
178
 
163
179
  Create `install.sh`:
@@ -231,7 +247,7 @@ Use `sarif` when the result should appear in GitHub Code Scanning or another
231
247
  SARIF-compatible viewer. Upload it explicitly with the official upload action:
232
248
 
233
249
  ```yaml
234
- - uses: Mresyzz/opsscript-gate@v0.8.3
250
+ - uses: Mresyzz/opsscript-gate@v0.9.1
235
251
  with:
236
252
  script-path: scripts/install.sh
237
253
  format: sarif
@@ -1,18 +1,25 @@
1
- # OpsScript Gate:GitHub Action 跨发行版 Shell 安装脚本测试
1
+ # OpsScript Gate:cross-distro shell script runtime compatibility checker
2
2
 
3
- [English](README.md) · [配置说明](docs/configuration.md) · [常见问题](docs/troubleshooting.md) · [自托管模型安装脚本指南](docs/gpt-oss.md)
3
+ OpsScript Gate 是跨发行版 Shell 脚本运行时兼容性检查器(cross-distro shell script runtime compatibility checker)。
4
+ 它在 Debian、Ubuntu、Alpine 中真实运行脚本,诊断 `command not found`。
5
+
6
+ [English](README.md) · [按错误排查:`command not found` / `apt-get: not found`](docs/command-not-found.md) · [配置说明](docs/configuration.md) · [常见问题](docs/troubleshooting.md) · [自托管模型安装脚本指南](docs/gpt-oss.md)
4
7
 
5
8
  在 Debian、Ubuntu、Alpine 容器里实际运行 Shell 脚本,提前发现缺失命令、
6
9
  Bash 依赖、包管理器假设和交互式阻塞。它与 ShellCheck 互补。
7
10
 
11
+ 如果你搜索的是“在 Debian/Ubuntu/Alpine 真实运行 shell 脚本”“Alpine 上
12
+ `apt-get: not found`”“ShellCheck 通过但 CI 运行失败”,请直接看
13
+ [按错误排查的指南](docs/command-not-found.md)。
14
+
8
15
  适合独立安装脚本、容器入口脚本和发布脚本。每次只挂载目标脚本,
9
16
  不挂载整个仓库;依赖其他仓库文件的脚本需要现有项目测试配合。
10
17
 
11
18
  也可以直接打开 [OpsScript Gate 在线演示](https://mresyzz.github.io/opsscript-gate/),先查看 ShellCheck 与运行时验证的差异,再复制 workflow 到自己的仓库。
12
19
 
13
- ## 当前版本 v0.8.3
20
+ ## 当前版本 v0.9.0
14
21
 
15
- v0.8.3 已作为正式版本发布;如果你要从源码验证当前分支,也可以执行:
22
+ v0.9.0 已作为正式版本发布;如果你要从源码验证当前分支,也可以执行:
16
23
 
17
24
  ```bash
18
25
  pip install -e .
@@ -28,7 +35,7 @@ opsscript-gate run --dry-run
28
35
  opsscript-gate run --format json --output reports/compatibility.json
29
36
  ```
30
37
 
31
- `init` 生成配置和 GitHub 工作流,不覆盖已有文件。工作流引用 `v0.8.3`。
38
+ `init` 生成配置和 GitHub 工作流,不覆盖已有文件。工作流引用 `v0.9.0`。
32
39
  预览不需要 Docker;真实运行需要 Python 3.10+
33
40
  和可访问的 Linux Docker 引擎。
34
41
  `opsscript-gate doctor` 可以在真实运行前检查 Python 和 Docker。
@@ -51,6 +58,8 @@ opsscript-gate run --format json --output reports/compatibility.json
51
58
  - 排除规则、发行版预设、扫描数量限制。
52
59
  - 超过扫描上限明确报错,避免只测前 20 个却误以为全部通过。
53
60
  - 报告保存为 JSON、Markdown 或文本,失败时同样保留结果。
61
+ - 使用 `--repeat 3` 或配置 `"repeat": 3` 重复运行每个发行版;同一发行版出现
62
+ 一次通过、一次失败时会标记为 `FLAKY`,避免偶发绿灯掩盖 CI 不稳定。
54
63
  - 自动发现跳过符号链接,并严格校验配置与执行参数。
55
64
 
56
65
  返回码 `0` 表示全部通过,`1` 表示检查失败或发生错误。`--dry-run` 成功只表示
@@ -65,7 +74,7 @@ Docker daemon 和可选的项目配置。
65
74
  - uses: actions/checkout@v7
66
75
  with:
67
76
  fetch-depth: 0
68
- - uses: Mresyzz/opsscript-gate@v0.8.3
77
+ - uses: Mresyzz/opsscript-gate@v0.9.0
69
78
  with:
70
79
  changed-since: ${{ github.event.pull_request.base.sha }}
71
80
  preset: minimal
@@ -4,6 +4,15 @@ This roadmap tracks features delivered in recent releases and areas under consid
4
4
 
5
5
  ---
6
6
 
7
+ ## Delivered in v0.9.0
8
+
9
+ - [x] **Flaky Runtime Detection (`--repeat` / `repeat`)**: Repeat each distribution
10
+ run and report mixed pass/fail outcomes as `FLAKY` with attempt counts.
11
+ - [x] **Bounded Configuration Loading**: Reject oversized project configuration files
12
+ before parsing them.
13
+ - [x] **Blocking Container Wait**: Prefer Docker's wait API over repeated synchronous
14
+ status reloads while retaining a test-double fallback.
15
+
7
16
  ## Delivered in v0.4.0
8
17
 
9
18
  - [x] **High-confidence Line-level GitHub Actions Annotations**: Emits safe `::error` annotations linking failure lines directly on pull request file diffs.
@@ -1,5 +1,5 @@
1
1
  name: 'OpsScript Gate'
2
- description: 'Run Linux shell installers across Debian, Ubuntu, and Alpine; a ShellCheck companion for runtime compatibility in CI.'
2
+ description: 'Cross-distro shell script compatibility checker for runtime testing in Debian, Ubuntu, and Alpine. Diagnoses command not found.'
3
3
  author: 'Mresyzz'
4
4
  branding:
5
5
  icon: 'shield'
@@ -22,6 +22,10 @@ inputs:
22
22
  description: 'Hard timeout in seconds per container (default: 60)'
23
23
  required: false
24
24
  default: ''
25
+ repeat:
26
+ description: 'Repeat each distribution run to detect flaky runtime behavior (default: 1)'
27
+ required: false
28
+ default: ''
25
29
  format:
26
30
  description: 'Output report format: table, markdown, json, or sarif'
27
31
  required: false
@@ -96,6 +100,7 @@ runs:
96
100
  INPUT_MATRIX: ${{ inputs.matrix }}
97
101
  INPUT_JOBS: ${{ inputs.jobs }}
98
102
  INPUT_TIMEOUT: ${{ inputs.timeout }}
103
+ INPUT_REPEAT: ${{ inputs.repeat }}
99
104
  INPUT_FORMAT: ${{ inputs.format }}
100
105
  INPUT_SHELL: ${{ inputs.shell }}
101
106
  INPUT_MEM_LIMIT: ${{ inputs.mem-limit }}
@@ -116,6 +121,7 @@ runs:
116
121
  fi
117
122
  # Only explicit inputs override project configuration.
118
123
  [ -z "$INPUT_TIMEOUT" ] || ARGS+=(--timeout "$INPUT_TIMEOUT")
124
+ [ -z "$INPUT_REPEAT" ] || ARGS+=(--repeat "$INPUT_REPEAT")
119
125
  [ -z "$INPUT_FORMAT" ] || ARGS+=(--format "$INPUT_FORMAT")
120
126
  [ -z "$INPUT_SHELL" ] || ARGS+=(--shell "$INPUT_SHELL")
121
127
  [ -z "$INPUT_MEM_LIMIT" ] || ARGS+=(--mem-limit "$INPUT_MEM_LIMIT")
@@ -232,7 +232,7 @@
232
232
  OpsScript Gate
233
233
  </span>
234
234
  <span class="font-mono text-[11px] px-1.5 py-0.5 rounded bg-[var(--surface-muted)] text-[var(--text-muted)] border border-[var(--border-light)]">
235
- v0.8.3
235
+ v0.9.0
236
236
  </span>
237
237
  </div>
238
238
  </div>
@@ -367,7 +367,7 @@
367
367
  <div class="panel-card p-4 flex flex-col justify-between space-y-3">
368
368
  <div class="space-y-2.5">
369
369
  <div class="flex items-center justify-between border-b border-[var(--border-light)] pb-2">
370
- <span class="font-mono text-xs font-medium text-[var(--text-muted)]">OpsScript Gate Runtime (v0.8.3)</span>
370
+ <span class="font-mono text-xs font-medium text-[var(--text-muted)]">OpsScript Gate Runtime (v0.9.0)</span>
371
371
  <span id="ops-status" class="font-mono text-xs font-semibold px-2 py-0.5 rounded border">
372
372
  1 DISTRO FAILED
373
373
  </span>
@@ -536,7 +536,7 @@
536
536
  <span class="font-semibold text-[var(--text-hero)]">steps:</span>
537
537
  - <span class="font-semibold text-[var(--text-hero)]">uses:</span> actions/checkout@v4
538
538
  - <span class="font-semibold text-[var(--text-hero)]">name:</span> Validate scripts across distributions
539
- <span class="font-semibold text-[var(--brand)]">uses:</span> Mresyzz/opsscript-gate@v0.8.3
539
+ <span class="font-semibold text-[var(--brand)]">uses:</span> Mresyzz/opsscript-gate@v0.9.0
540
540
  <span class="font-semibold text-[var(--text-hero)]">with:</span>
541
541
  <span class="text-[var(--text-body)]">script-path:</span> ./install.sh
542
542
  <span class="text-[var(--text-body)]">shell:</span> auto
@@ -604,7 +604,7 @@ jobs:
604
604
  steps:
605
605
  - uses: actions/checkout@v4
606
606
  - name: Validate scripts across distributions
607
- uses: Mresyzz/opsscript-gate@v0.8.3
607
+ uses: Mresyzz/opsscript-gate@v0.9.0
608
608
  with:
609
609
  script-path: ./install.sh
610
610
  shell: auto
@@ -0,0 +1,122 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Test shell scripts in Debian, Ubuntu and Alpine: command not found | OpsScript Gate</title>
7
+ <meta name="description" content="Test a shell installer in real Debian, Ubuntu and Alpine containers. Diagnose command not found, exit code 127 and missing Bash with OpsScript Gate, a GitHub Action and CLI.">
8
+ <link rel="canonical" href="https://mresyzz.github.io/opsscript-gate/command-not-found.html">
9
+ <meta property="og:title" content="ShellCheck passed. Does your installer run on Alpine?">
10
+ <meta property="og:description" content="A reproducible shell runtime compatibility check for Debian, Ubuntu and Alpine, with a copyable GitHub Actions workflow.">
11
+ <meta property="og:url" content="https://mresyzz.github.io/opsscript-gate/command-not-found.html">
12
+ <style>
13
+ :root { color-scheme: light; --ink:#262420; --muted:#59544b; --accent:#a94a31; --line:#ddd6c9; }
14
+ * { box-sizing:border-box; }
15
+ body { margin:0; background:#faf8f5; color:var(--ink); font:17px/1.7 system-ui,sans-serif; }
16
+ main { width:min(900px,100%); margin:auto; padding:32px 24px 64px; }
17
+ nav { display:flex; flex-wrap:wrap; justify-content:space-between; gap:12px; padding-bottom:28px; }
18
+ a { color:var(--accent); text-underline-offset:4px; }
19
+ a:focus-visible { outline:3px solid var(--accent); outline-offset:5px; }
20
+ h1 { font:normal clamp(32px,6vw,48px)/1.18 Georgia,serif; letter-spacing:-.025em; margin:12px 0 20px; }
21
+ h2 { font:normal 28px/1.3 Georgia,serif; margin:38px 0 12px; }
22
+ h3 { font-size:18px; margin:0 0 10px; }
23
+ p { margin:12px 0; } .eyebrow,.muted { color:var(--muted); }
24
+ .eyebrow { font-size:13px; letter-spacing:.05em; text-transform:uppercase; }
25
+ pre { background:#f0ebe2; padding:20px; border:1px solid var(--line); border-radius:9px; overflow:auto; font:14px/1.65 Consolas,monospace; }
26
+ code { font-family:Consolas,monospace; } p code,td code { font-size:.9em; }
27
+ .table-wrap { overflow:auto; } table { border-collapse:collapse; width:100%; }
28
+ th,td { text-align:left; padding:12px; border-bottom:1px solid var(--line); }
29
+ .pass { color:#1e593d; } .fail { color:#8a342b; }
30
+ .cards { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:14px; }
31
+ .card { border:1px solid var(--line); background:white; border-radius:9px; padding:20px; font-size:15px; }
32
+ .cta { display:inline-block; background:var(--accent); color:white; padding:10px 18px; border-radius:6px; text-decoration:none; margin:16px 0; }
33
+ footer { margin-top:40px; padding-top:20px; border-top:1px solid var(--line); font-size:14px; }
34
+ @media (max-width:650px) { .cards { grid-template-columns:1fr; } main { padding:24px 18px 48px; } }
35
+ </style>
36
+ </head>
37
+ <body>
38
+ <main>
39
+ <nav aria-label="Primary"><a href="./">OpsScript Gate</a><a href="https://github.com/Mresyzz/opsscript-gate">GitHub repository</a></nav>
40
+ <p class="eyebrow">Cross-distro shell script runtime compatibility checker</p>
41
+ <h1>ShellCheck passed.<br>Your installer still failed on Alpine.</h1>
42
+ <p><strong>OpsScript Gate tests shell scripts in real Debian, Ubuntu, and Alpine containers.</strong>
43
+ It is a GitHub Action and Python CLI that reports each distribution's PASS/FAIL result and
44
+ diagnoses runtime failures such as <code>command not found</code>.</p>
45
+ <p>Use it for standalone installers, bootstrap scripts, and shell entrypoints. You supply the script;
46
+ the tool runs the distribution matrix and collects the diagnostic report.</p>
47
+ <a class="cta" href="#workflow">Add the runtime check to CI</a>
48
+
49
+ <h2>A valid shell script can assume the wrong command</h2>
50
+ <p>Save this as <code>install.sh</code>. The syntax is valid POSIX shell, but Alpine uses
51
+ <code>apk</code> and does not supply <code>apt-get</code> in its standard image.</p>
52
+ <pre><code>#!/bin/sh
53
+ set -eu
54
+ apt-get --version</code></pre>
55
+ <p class="muted">Expected outcome for this example; this page does not execute containers in your browser.</p>
56
+ <div class="table-wrap"><table>
57
+ <thead><tr><th scope="col">Image</th><th scope="col">Result</th><th scope="col">Diagnostic</th></tr></thead>
58
+ <tbody>
59
+ <tr><td><code>debian:12-slim</code></td><td class="pass">PASS</td><td>Exit 0</td></tr>
60
+ <tr><td><code>ubuntu:22.04</code></td><td class="pass">PASS</td><td>Exit 0</td></tr>
61
+ <tr><td><code>ubuntu:24.04</code></td><td class="pass">PASS</td><td>Exit 0</td></tr>
62
+ <tr><td><code>alpine:3.20</code></td><td class="fail">FAIL</td><td>Exit 127: <code>apt-get: not found</code>, line 3</td></tr>
63
+ </tbody>
64
+ </table></div>
65
+ <p>A shell linter checks source code. Executing the script tests which commands actually exist in the target image.
66
+ Run both checks to cover source mistakes and runtime assumptions.</p>
67
+
68
+ <h2 id="workflow">A complete GitHub Actions workflow</h2>
69
+ <p>Copy this into <code>.github/workflows/shell-runtime.yml</code> and set <code>script-path</code>
70
+ to your installer. The Ubuntu runner starts the Debian, Ubuntu, and Alpine containers.</p>
71
+ <pre><code>name: Shell runtime compatibility
72
+ on: [push, pull_request]
73
+ permissions:
74
+ contents: read
75
+ jobs:
76
+ runtime:
77
+ runs-on: ubuntu-latest
78
+ steps:
79
+ - uses: actions/checkout@v7
80
+ with:
81
+ persist-credentials: false
82
+ - uses: Mresyzz/opsscript-gate@v0.9.0
83
+ with:
84
+ script-path: install.sh
85
+ shell: auto
86
+ format: markdown
87
+ output: reports/compatibility.md</code></pre>
88
+ <p>The report includes the image, exit code, script line where available, missing command, and remediation hint.
89
+ GitHub Actions also receives workflow annotations and a Step Summary.
90
+ <a href="https://github.com/Mresyzz/opsscript-gate/actions/workflows/demo.yml">Inspect the project's real CI demo runs</a>.</p>
91
+
92
+ <h2>What the failure tells you</h2>
93
+ <div class="cards">
94
+ <article class="card"><h3><code>apt-get: not found</code></h3><p>Check for APT or <code>apk</code> before calling it.
95
+ Package names differ by distribution; do not replace them blindly.</p></article>
96
+ <article class="card"><h3><code>/bin/bash: not found</code></h3><p>Minimal Alpine omits Bash by default.
97
+ Use POSIX shell or declare an image or package prerequisite that supplies Bash.</p></article>
98
+ <article class="card"><h3><code>curl</code> or <code>jq</code> missing</h3><p>Minimal images omit many convenience tools.
99
+ Declare required tools through <code>packages</code> or explain the prerequisite in your installer.</p></article>
100
+ </div>
101
+
102
+ <h2>Run the same check locally</h2>
103
+ <p>Requires Python 3.10+ and a reachable Docker engine running Linux containers.</p>
104
+ <pre><code>pip install opsscript-gate
105
+ opsscript-gate doctor
106
+ opsscript-gate run ./install.sh</code></pre>
107
+ <p>The CLI supports table, Markdown, JSON, and SARIF reports.
108
+ <code>--dry-run</code> previews the plan without Docker; it does not verify runtime compatibility.</p>
109
+
110
+ <h2>Where this check fits</h2>
111
+ <p>ShellCheck analyzes shell source; OpsScript Gate executes it across distributions. A handwritten Docker matrix
112
+ can also run scripts, but you maintain its image list, timeouts, log parsing, and reports.</p>
113
+ <p>OpsScript Gate mounts each script independently. Sibling files and repository dependencies are not mounted.
114
+ These are distribution user-space tests in containers, not separate Linux kernels or full-machine tests.
115
+ Passing checks do not prove security or replace project integration tests.</p>
116
+ <footer>Read the <a href="https://github.com/Mresyzz/opsscript-gate/blob/main/docs/configuration.md">configuration guide</a>,
117
+ <a href="https://github.com/Mresyzz/opsscript-gate/blob/main/docs/troubleshooting.md">troubleshooting</a>,
118
+ or <a href="https://github.com/Mresyzz/opsscript-gate/blob/main/SECURITY.md">security boundaries</a>.
119
+ <a href="./">Open the interactive demo</a>.</footer>
120
+ </main>
121
+ </body>
122
+ </html>