opsscript-gate 0.9.1__tar.gz → 0.9.4__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 (62) hide show
  1. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/.github/workflow-templates/opsscript-gate.yml +1 -1
  2. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/.github/workflows/release.yml +3 -7
  3. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/.gitignore +1 -0
  4. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/CHANGELOG.md +43 -0
  5. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/CITATION.cff +1 -2
  6. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/PKG-INFO +16 -13
  7. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/README.md +12 -10
  8. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/README.zh-CN.md +4 -4
  9. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/action.yml +7 -6
  10. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/demo.html +4 -4
  11. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/docs/command-not-found.html +70 -1
  12. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/docs/command-not-found.md +25 -1
  13. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/docs/configuration.md +7 -4
  14. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/docs/gpt-oss.md +1 -1
  15. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/docs/index.html +20 -4
  16. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/docs/llms.txt +8 -1
  17. opsscript_gate-0.9.4/docs/robots.txt +3 -0
  18. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/examples/github-actions/README.md +1 -1
  19. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/examples/github-actions/workflow.yml +1 -1
  20. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/index.html +20 -4
  21. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/llms.txt +8 -1
  22. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/pyproject.toml +6 -2
  23. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/src/opsscript_gate/__init__.py +1 -1
  24. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/src/opsscript_gate/cli.py +79 -11
  25. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/src/opsscript_gate/config.py +39 -3
  26. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/src/opsscript_gate/discovery.py +40 -8
  27. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/src/opsscript_gate/models.py +23 -12
  28. opsscript_gate-0.9.4/src/opsscript_gate/remediation.py +239 -0
  29. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/src/opsscript_gate/reporter.py +116 -56
  30. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/src/opsscript_gate/runner.py +286 -96
  31. opsscript_gate-0.9.4/tests/conftest.py +17 -0
  32. opsscript_gate-0.9.4/tests/test_action_contract.py +45 -0
  33. opsscript_gate-0.9.4/tests/test_audit_coverage.py +577 -0
  34. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/tests/test_onboarding.py +73 -1
  35. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/tests/test_runner.py +459 -7
  36. opsscript_gate-0.9.1/src/opsscript_gate/remediation.py +0 -104
  37. opsscript_gate-0.9.1/tests/test_action_contract.py +0 -24
  38. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/.gitattributes +0 -0
  39. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  40. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/.github/ISSUE_TEMPLATE/distro_request.yml +0 -0
  41. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  42. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/.github/assets/social-preview.png +0 -0
  43. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/.github/pull_request_template.md +0 -0
  44. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/.github/workflow-templates/opsscript-gate.properties.json +0 -0
  45. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/.github/workflows/demo.yml +0 -0
  46. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/.github/workflows/deploy-pages.yml +0 -0
  47. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/.github/workflows/test.yml +0 -0
  48. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/CONTRIBUTING.md +0 -0
  49. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/LICENSE +0 -0
  50. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/ROADMAP.md +0 -0
  51. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/SECURITY.md +0 -0
  52. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/docs/sitemap.xml +0 -0
  53. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/docs/troubleshooting.md +0 -0
  54. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/examples/alpine-incompatibility/README.md +0 -0
  55. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/examples/alpine-incompatibility/apt_dependency.sh +0 -0
  56. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/examples/basic/README.md +0 -0
  57. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/examples/basic/clean_setup.sh +0 -0
  58. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/examples/interactive-hang/README.md +0 -0
  59. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/examples/interactive-hang/prompt_confirm.sh +0 -0
  60. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/tests/fixtures/fail_deps.sh +0 -0
  61. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/tests/fixtures/fail_hang.sh +0 -0
  62. {opsscript_gate-0.9.1 → opsscript_gate-0.9.4}/tests/fixtures/pass_basic.sh +0 -0
@@ -25,7 +25,7 @@ jobs:
25
25
  persist-credentials: false
26
26
 
27
27
  - name: Run OpsScript Gate
28
- uses: Mresyzz/opsscript-gate@v0.8.3
28
+ uses: Mresyzz/opsscript-gate@v0.9.2
29
29
  with:
30
30
  shell: auto
31
31
  network: none
@@ -123,16 +123,12 @@ jobs:
123
123
  TAG_SHA=$(git rev-list -n 1 "$TARGET_TAG")
124
124
  echo "Tag $TARGET_TAG already exists, pointing to commit: $TAG_SHA"
125
125
  if [ "$TAG_SHA" = "${{ github.sha }}" ]; then
126
- echo "Tag $TARGET_TAG points to current commit (${{ github.sha }}). Treating as retry/resume of release."
127
- IS_NEW_RELEASE="true"
128
- SHOULD_TAG="false"
126
+ echo "Tag $TARGET_TAG already points to the current commit. The tag workflow owns release publication."
129
127
  else
130
128
  echo "Tag $TARGET_TAG was released from earlier commit ($TAG_SHA != ${{ github.sha }}). Skipping release."
131
- IS_NEW_RELEASE="false"
132
- SHOULD_TAG="false"
133
129
  fi
134
130
  else
135
- echo "Target tag $TARGET_TAG does not exist. Scheduling new release for commit ${{ github.sha }}."
131
+ echo "Target tag $TARGET_TAG does not exist. Creating tag and publishing release."
136
132
  IS_NEW_RELEASE="true"
137
133
  SHOULD_TAG="true"
138
134
  fi
@@ -171,7 +167,7 @@ jobs:
171
167
  retention-days: 7
172
168
 
173
169
  - name: Create and push Git tag
174
- if: steps.release-check.outputs.is_new_release == 'true' && steps.release-check.outputs.should_tag == 'true'
170
+ if: steps.release-check.outputs.should_tag == 'true'
175
171
  shell: bash
176
172
  run: |
177
173
  TARGET_TAG="${{ steps.release-check.outputs.release_tag }}"
@@ -50,3 +50,4 @@ venv.bak/
50
50
  *~
51
51
  .DS_Store
52
52
  Thumbs.db
53
+ .ai/
@@ -1,5 +1,48 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.4] - 2026-10-11
4
+
5
+ ### Fixed
6
+ - Fixed release workflow publishing deadlock where `main` pushes skipped distribution package builds and release creation due to GITHUB_TOKEN tag events not triggering new workflows.
7
+ - Unified command path extraction in remediation rules to ensure full-path commands (e.g. `/usr/bin/apt-get`, `/sbin/apk`, `/bin/yum`, `/usr/local/bin/bash`) accurately match package manager and shell hints.
8
+ - Added native RHEL/Fedora/CentOS package mappings in `COMMON_COMMAND_PACKAGES` to prevent fallback to invalid Debian package names.
9
+ - Added native container package pre-installation support for `dnf`, `microdnf`, and `yum` in `runner._wrap_with_package_setup`.
10
+ - Fixed `RunReport` and `MultiScriptReport` `__post_init__` to preserve explicitly passed `all_passed=False` when result collections are empty.
11
+ - Supported bare directory exclusions (e.g. `tests` or `tests/`) in `_matches_exclude` to match both directories and all contained files.
12
+
13
+ ## [0.9.3] - 2026-10-11
14
+
15
+ ### Added
16
+ - Added cross-distribution package name mapping and actionable remediation suggestions (`apt-get`, `apk`, `dnf`) for common missing operations commands (`curl`, `wget`, `jq`, `git`, `tar`, `python3`, `pip`, `sudo`, `make`, `gcc`, etc.).
17
+ - Extended `FailureDiagnostic` and SARIF output with structured `suggested_package` property.
18
+
19
+ ### Fixed
20
+ - Fixed unreadable shell script candidate misclassification during discovery.
21
+ - Ensured temporary script files are safely cleaned up during distribution preparation errors.
22
+ - Added preflight validation to reject directory and missing script paths before creating worker threads.
23
+ - Enforced consistent POSIX LF line endings in report output files across platforms.
24
+
25
+ ## [0.9.2] - 2026-10-05
26
+
27
+ ### Fixed
28
+ - Hardened SARIF, Markdown, terminal, and machine-readable output handling.
29
+ - Prevented false package-setup errors, stale Docker wait results, hard-link overwrites, and unbounded log copies.
30
+ - Added BOM-aware discovery, Windows exclusion normalization, bounded concurrency, and runtime option validation.
31
+ - Added a one-CPU container limit and bounded Docker log rotation, while keeping disposable container filesystems writable for installers.
32
+ - Normalized SARIF paths, preserved actionable repeat-run diagnostics, skipped hidden auto-discovered files, and kept terminal reports visible when file output fails.
33
+ - Fixed release workflow tag handoff so a `main` push cannot publish the same version twice.
34
+ - Made `doctor` reject Docker's Windows-container mode before Linux image checks begin.
35
+ - Added problem-first FAQ content, structured data, canonical URLs, and AI-readable answers for runtime compatibility searches.
36
+ - Bounded repeat runs to 20 attempts per distribution to prevent accidental unbounded container work and result retention.
37
+ - Bounded per-container timeouts to one hour so fallback polling cannot wait indefinitely on an extreme configuration.
38
+ - Bounded custom matrices to 32 images so a single configuration cannot create unbounded container work.
39
+ - Fixed `_is_ignored_path` in script discovery to avoid misclassifying paths with leading `./` as ignored.
40
+ - Traversed exception cause chains in `_is_wait_timeout_error` to reliably identify nested Docker wait timeouts.
41
+ - Protected container reload against network jitter to preserve valid exit codes.
42
+ - Added early validation for CLI `--output` to reject existing directories before executing test matrices.
43
+ - Expanded cross-distro package manager diagnostics and added remediation for path-qualified curl/wget and missing sudo.
44
+ - Refreshed active workflow templates, examples, and documentation to `v0.9.2`.
45
+
3
46
  ## [0.9.1] - 2026-10-03
4
47
 
5
48
  ### Fixed
@@ -5,8 +5,7 @@ type: software
5
5
  authors:
6
6
  - family-names: Mresyzz
7
7
  given-names: Mresy
8
- version: 0.9.0
9
- date-released: 2026-10-02
8
+ version: 0.9.2
10
9
  repository-code: https://github.com/Mresyzz/opsscript-gate
11
10
  url: https://github.com/Mresyzz/opsscript-gate
12
11
  license: MIT
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.5
2
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
3
+ Version: 0.9.4
4
+ Summary: Cross-distro shell script compatibility and portability checker for Debian, Ubuntu, and Alpine. Runs real containers to diagnose command not found.
5
5
  Project-URL: Homepage, https://mresyzz.github.io/opsscript-gate/
6
6
  Project-URL: Demo, https://mresyzz.github.io/opsscript-gate/
7
7
  Project-URL: Repository, https://github.com/Mresyzz/opsscript-gate
@@ -12,7 +12,7 @@ Project-URL: Troubleshooting, https://github.com/Mresyzz/opsscript-gate/blob/mai
12
12
  Author: Mresyzz
13
13
  License: MIT
14
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
15
+ Keywords: alpine,apk,apt-get,bashisms,bootstrap,busybox,ci,command-not-found,compatibility,cross-distro,debian,devops,distribution-compatibility,docker,github-actions,installer,installer-testing,linux,portable-shell,posix,real-container-testing,runtime-compatibility,shell,shell-portability,shell-runtime,shell-script-testing,shellcheck,shellcheck-alternative,testing,ubuntu
16
16
  Classifier: Development Status :: 4 - Beta
17
17
  Classifier: Intended Audience :: Developers
18
18
  Classifier: Intended Audience :: System Administrators
@@ -26,6 +26,7 @@ Classifier: Topic :: System :: Systems Administration
26
26
  Requires-Python: >=3.10
27
27
  Requires-Dist: docker>=7.0.0
28
28
  Provides-Extra: test
29
+ Requires-Dist: markdown-it-py>=3.0.0; extra == 'test'
29
30
  Requires-Dist: pytest>=8.0.0; extra == 'test'
30
31
  Description-Content-Type: text/markdown
31
32
 
@@ -79,7 +80,7 @@ apt-get --version
79
80
  Run it locally with `opsscript-gate run ./install.sh`, or add the following step to CI:
80
81
 
81
82
  ```yaml
82
- - uses: Mresyzz/opsscript-gate@v0.9.1
83
+ - uses: Mresyzz/opsscript-gate@v0.9.2
83
84
  with:
84
85
  script-path: install.sh
85
86
  ```
@@ -88,7 +89,7 @@ The result identifies the failing distribution, exit code, line, missing command
88
89
 
89
90
  ---
90
91
 
91
- ## Current release: v0.9.1
92
+ ## Current version: v0.9.2 (local candidate)
92
93
 
93
94
  The current release includes a project config file, a dry-run plan, presets, exclusions,
94
95
  saved reports, changed-script selection for pull requests, and repeat runs for detecting
@@ -142,7 +143,7 @@ jobs:
142
143
  - uses: actions/checkout@v7
143
144
  with:
144
145
  persist-credentials: false
145
- - uses: Mresyzz/opsscript-gate@v0.9.1
146
+ - uses: Mresyzz/opsscript-gate@v0.9.2
146
147
  ```
147
148
 
148
149
  This zero-configuration form discovers shell scripts in the repository. Use
@@ -153,7 +154,7 @@ repository has a single installer. For self-hosted model installers, see the
153
154
  Or test a specific script with custom execution modes:
154
155
 
155
156
  ```yaml
156
- - uses: Mresyzz/opsscript-gate@v0.9.1
157
+ - uses: Mresyzz/opsscript-gate@v0.9.2
157
158
  with:
158
159
  script-path: scripts/install.sh
159
160
  shell: auto
@@ -187,7 +188,7 @@ unrelated scripts:
187
188
  with:
188
189
  persist-credentials: false
189
190
  fetch-depth: 0
190
- - uses: Mresyzz/opsscript-gate@v0.9.1
191
+ - uses: Mresyzz/opsscript-gate@v0.9.2
191
192
  with:
192
193
  changed-since: ${{ github.event.pull_request.base.sha }}
193
194
  preset: minimal
@@ -199,8 +200,8 @@ container. The same selection can be previewed locally with
199
200
 
200
201
  ### Detect intermittent runtime failures
201
202
 
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
203
+ Use `--repeat N` when a script sometimes passes and sometimes fails in CI. Values from
204
+ 1 through 20 are accepted. OpsScript Gate runs each distribution N times and reports `FLAKY` when the same distribution has
204
205
  both passing and failing attempts. The JSON, Markdown, SARIF, annotations, and step
205
206
  summary include the attempt counts so a retry cannot silently turn an unstable script
206
207
  green.
@@ -278,7 +279,7 @@ Use `sarif` when the result should appear in GitHub Code Scanning or another
278
279
  SARIF-compatible viewer. Upload it explicitly with the official upload action:
279
280
 
280
281
  ```yaml
281
- - uses: Mresyzz/opsscript-gate@v0.9.1
282
+ - uses: Mresyzz/opsscript-gate@v0.9.2
282
283
  with:
283
284
  script-path: scripts/install.sh
284
285
  format: sarif
@@ -373,9 +374,11 @@ The runner uses restricted container defaults:
373
374
  2. **Resource Constraints**:
374
375
  - Memory limits enforced per container (`--mem-limit`, default: `256m`).
375
376
  - Process caps enforced to prevent fork bombs (`--pids-limit`, default: `128`).
377
+ - CPU is capped at one host CPU per container and Docker's JSON log driver rotates logs.
376
378
  - Network isolation configurable (`--network bridge` or `--network none`).
377
379
  3. **Read-Only Target Mount**:
378
380
  - The tested script is mounted read-only (`:ro`) at `/tmp/target_script.sh`.
381
+ - The disposable container root remains writable so installers can create files during a real run.
379
382
  - No host directories or sensitive sockets are mounted into test containers.
380
383
  4. **Anti-Hang Deadlock Defense**:
381
384
  - Disables TTY and stdin (`stdin_open=False`, `tty=False`).
@@ -448,8 +451,8 @@ usage: opsscript-gate run [-h] [--matrix MATRIX] [-j JOBS] [--timeout TIMEOUT]
448
451
  | :--- | :--- | :--- | :--- |
449
452
  | `script_path` | Positional | *Optional* | Path to target shell script (auto-discovers if omitted) |
450
453
  | `-j, --jobs` | Integer | `min(2, size)` | Number of concurrent container jobs |
451
- | `--matrix` | String | `debian:12-slim,ubuntu:22.04,ubuntu:24.04,alpine:3.20` | Comma-separated list of Docker images |
452
- | `--timeout` | Integer | `60` | Hard timeout per container in seconds |
454
+ | `--matrix` | String | `debian:12-slim,ubuntu:22.04,ubuntu:24.04,alpine:3.20` | Comma-separated list of Docker images (maximum `32`) |
455
+ | `--timeout` | Integer | `60` | Hard timeout per container in seconds (maximum `3600`) |
453
456
  | `--format` | Choice | `table` | Output format: `table`, `markdown`, `json`, or `sarif` |
454
457
  | `--shell` | Choice | `posix` | Execution mode: `posix` (default), `shebang`, or `auto` |
455
458
  | `--mem-limit` | String | `256m` | Memory limit per container (e.g. `256m`, `512m`) |
@@ -48,7 +48,7 @@ apt-get --version
48
48
  Run it locally with `opsscript-gate run ./install.sh`, or add the following step to CI:
49
49
 
50
50
  ```yaml
51
- - uses: Mresyzz/opsscript-gate@v0.9.1
51
+ - uses: Mresyzz/opsscript-gate@v0.9.2
52
52
  with:
53
53
  script-path: install.sh
54
54
  ```
@@ -57,7 +57,7 @@ The result identifies the failing distribution, exit code, line, missing command
57
57
 
58
58
  ---
59
59
 
60
- ## Current release: v0.9.1
60
+ ## Current version: v0.9.2 (local candidate)
61
61
 
62
62
  The current release includes a project config file, a dry-run plan, presets, exclusions,
63
63
  saved reports, changed-script selection for pull requests, and repeat runs for detecting
@@ -111,7 +111,7 @@ jobs:
111
111
  - uses: actions/checkout@v7
112
112
  with:
113
113
  persist-credentials: false
114
- - uses: Mresyzz/opsscript-gate@v0.9.1
114
+ - uses: Mresyzz/opsscript-gate@v0.9.2
115
115
  ```
116
116
 
117
117
  This zero-configuration form discovers shell scripts in the repository. Use
@@ -122,7 +122,7 @@ repository has a single installer. For self-hosted model installers, see the
122
122
  Or test a specific script with custom execution modes:
123
123
 
124
124
  ```yaml
125
- - uses: Mresyzz/opsscript-gate@v0.9.1
125
+ - uses: Mresyzz/opsscript-gate@v0.9.2
126
126
  with:
127
127
  script-path: scripts/install.sh
128
128
  shell: auto
@@ -156,7 +156,7 @@ unrelated scripts:
156
156
  with:
157
157
  persist-credentials: false
158
158
  fetch-depth: 0
159
- - uses: Mresyzz/opsscript-gate@v0.9.1
159
+ - uses: Mresyzz/opsscript-gate@v0.9.2
160
160
  with:
161
161
  changed-since: ${{ github.event.pull_request.base.sha }}
162
162
  preset: minimal
@@ -168,8 +168,8 @@ container. The same selection can be previewed locally with
168
168
 
169
169
  ### Detect intermittent runtime failures
170
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
171
+ Use `--repeat N` when a script sometimes passes and sometimes fails in CI. Values from
172
+ 1 through 20 are accepted. OpsScript Gate runs each distribution N times and reports `FLAKY` when the same distribution has
173
173
  both passing and failing attempts. The JSON, Markdown, SARIF, annotations, and step
174
174
  summary include the attempt counts so a retry cannot silently turn an unstable script
175
175
  green.
@@ -247,7 +247,7 @@ Use `sarif` when the result should appear in GitHub Code Scanning or another
247
247
  SARIF-compatible viewer. Upload it explicitly with the official upload action:
248
248
 
249
249
  ```yaml
250
- - uses: Mresyzz/opsscript-gate@v0.9.1
250
+ - uses: Mresyzz/opsscript-gate@v0.9.2
251
251
  with:
252
252
  script-path: scripts/install.sh
253
253
  format: sarif
@@ -342,9 +342,11 @@ The runner uses restricted container defaults:
342
342
  2. **Resource Constraints**:
343
343
  - Memory limits enforced per container (`--mem-limit`, default: `256m`).
344
344
  - Process caps enforced to prevent fork bombs (`--pids-limit`, default: `128`).
345
+ - CPU is capped at one host CPU per container and Docker's JSON log driver rotates logs.
345
346
  - Network isolation configurable (`--network bridge` or `--network none`).
346
347
  3. **Read-Only Target Mount**:
347
348
  - The tested script is mounted read-only (`:ro`) at `/tmp/target_script.sh`.
349
+ - The disposable container root remains writable so installers can create files during a real run.
348
350
  - No host directories or sensitive sockets are mounted into test containers.
349
351
  4. **Anti-Hang Deadlock Defense**:
350
352
  - Disables TTY and stdin (`stdin_open=False`, `tty=False`).
@@ -417,8 +419,8 @@ usage: opsscript-gate run [-h] [--matrix MATRIX] [-j JOBS] [--timeout TIMEOUT]
417
419
  | :--- | :--- | :--- | :--- |
418
420
  | `script_path` | Positional | *Optional* | Path to target shell script (auto-discovers if omitted) |
419
421
  | `-j, --jobs` | Integer | `min(2, size)` | Number of concurrent container jobs |
420
- | `--matrix` | String | `debian:12-slim,ubuntu:22.04,ubuntu:24.04,alpine:3.20` | Comma-separated list of Docker images |
421
- | `--timeout` | Integer | `60` | Hard timeout per container in seconds |
422
+ | `--matrix` | String | `debian:12-slim,ubuntu:22.04,ubuntu:24.04,alpine:3.20` | Comma-separated list of Docker images (maximum `32`) |
423
+ | `--timeout` | Integer | `60` | Hard timeout per container in seconds (maximum `3600`) |
422
424
  | `--format` | Choice | `table` | Output format: `table`, `markdown`, `json`, or `sarif` |
423
425
  | `--shell` | Choice | `posix` | Execution mode: `posix` (default), `shebang`, or `auto` |
424
426
  | `--mem-limit` | String | `256m` | Memory limit per container (e.g. `256m`, `512m`) |
@@ -17,9 +17,9 @@ Bash 依赖、包管理器假设和交互式阻塞。它与 ShellCheck 互补。
17
17
 
18
18
  也可以直接打开 [OpsScript Gate 在线演示](https://mresyzz.github.io/opsscript-gate/),先查看 ShellCheck 与运行时验证的差异,再复制 workflow 到自己的仓库。
19
19
 
20
- ## 当前版本 v0.9.0
20
+ ## 当前版本 v0.9.2
21
21
 
22
- v0.9.0 已作为正式版本发布;如果你要从源码验证当前分支,也可以执行:
22
+ v0.9.2 是当前本地候选版本;如果你要从源码验证当前分支,也可以执行:
23
23
 
24
24
  ```bash
25
25
  pip install -e .
@@ -35,7 +35,7 @@ opsscript-gate run --dry-run
35
35
  opsscript-gate run --format json --output reports/compatibility.json
36
36
  ```
37
37
 
38
- `init` 生成配置和 GitHub 工作流,不覆盖已有文件。工作流引用 `v0.9.0`。
38
+ `init` 生成配置和 GitHub 工作流,不覆盖已有文件。工作流引用 `v0.9.2`。
39
39
  预览不需要 Docker;真实运行需要 Python 3.10+
40
40
  和可访问的 Linux Docker 引擎。
41
41
  `opsscript-gate doctor` 可以在真实运行前检查 Python 和 Docker。
@@ -74,7 +74,7 @@ Docker daemon 和可选的项目配置。
74
74
  - uses: actions/checkout@v7
75
75
  with:
76
76
  fetch-depth: 0
77
- - uses: Mresyzz/opsscript-gate@v0.9.0
77
+ - uses: Mresyzz/opsscript-gate@v0.9.2
78
78
  with:
79
79
  changed-since: ${{ github.event.pull_request.base.sha }}
80
80
  preset: minimal
@@ -1,5 +1,5 @@
1
1
  name: 'OpsScript Gate'
2
- description: 'Cross-distro shell script compatibility checker for runtime testing in Debian, Ubuntu, and Alpine. Diagnoses command not found.'
2
+ description: 'Cross-distro shell script compatibility and portability checker for Debian, Ubuntu, and Alpine. Runs real containers to diagnose command not found.'
3
3
  author: 'Mresyzz'
4
4
  branding:
5
5
  icon: 'shield'
@@ -11,7 +11,7 @@ inputs:
11
11
  required: false
12
12
  default: ''
13
13
  matrix:
14
- description: 'Comma-separated container images to test (e.g. debian:12-slim,ubuntu:22.04,ubuntu:24.04,alpine:3.20)'
14
+ description: 'Comma-separated container images to test (maximum: 32; e.g. debian:12-slim,ubuntu:22.04,ubuntu:24.04,alpine:3.20)'
15
15
  required: false
16
16
  default: ''
17
17
  jobs:
@@ -19,11 +19,11 @@ inputs:
19
19
  required: false
20
20
  default: ''
21
21
  timeout:
22
- description: 'Hard timeout in seconds per container (default: 60)'
22
+ description: 'Hard timeout in seconds per container (default: 60, maximum: 3600)'
23
23
  required: false
24
24
  default: ''
25
25
  repeat:
26
- description: 'Repeat each distribution run to detect flaky runtime behavior (default: 1)'
26
+ description: 'Repeat each distribution run to detect flaky runtime behavior (default: 1, maximum: 20)'
27
27
  required: false
28
28
  default: ''
29
29
  format:
@@ -139,9 +139,10 @@ runs:
139
139
  pattern="${pattern%$'\r'}"
140
140
  [ -z "$pattern" ] || ARGS+=("--exclude" "$pattern")
141
141
  done <<< "$INPUT_EXCLUDE"
142
- if [ "$INPUT_DRY_RUN" = "true" ]; then
142
+ DRY_RUN_VALUE="$(printf '%s' "$INPUT_DRY_RUN" | tr '[:upper:]' '[:lower:]')"
143
+ if [ "$DRY_RUN_VALUE" = "true" ]; then
143
144
  ARGS+=("--dry-run")
144
- elif [ "$INPUT_DRY_RUN" != "false" ]; then
145
+ elif [ "$DRY_RUN_VALUE" != "false" ]; then
145
146
  echo "dry-run must be true or false" >&2
146
147
  exit 1
147
148
  fi
@@ -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.9.0
235
+ v0.9.2
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.9.0)</span>
370
+ <span class="font-mono text-xs font-medium text-[var(--text-muted)]">OpsScript Gate Runtime (v0.9.2)</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.9.0
539
+ <span class="font-semibold text-[var(--brand)]">uses:</span> Mresyzz/opsscript-gate@v0.9.2
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.9.0
607
+ uses: Mresyzz/opsscript-gate@v0.9.2
608
608
  with:
609
609
  script-path: ./install.sh
610
610
  shell: auto
@@ -5,6 +5,61 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>Test shell scripts in Debian, Ubuntu and Alpine: command not found | OpsScript Gate</title>
7
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
+ <script type="application/ld+json">
9
+ {
10
+ "@context": "https://schema.org",
11
+ "@type": "SoftwareApplication",
12
+ "name": "OpsScript Gate",
13
+ "applicationCategory": "DeveloperApplication",
14
+ "operatingSystem": "Linux",
15
+ "description": "Run shell scripts in real Debian, Ubuntu, and Alpine containers to diagnose command not found and cross-distro runtime failures.",
16
+ "url": "https://mresyzz.github.io/opsscript-gate/command-not-found.html",
17
+ "codeRepository": "https://github.com/Mresyzz/opsscript-gate",
18
+ "downloadUrl": "https://pypi.org/project/opsscript-gate/",
19
+ "isAccessibleForFree": true,
20
+ "license": "https://opensource.org/licenses/MIT"
21
+ }
22
+ </script>
23
+ <script type="application/ld+json">
24
+ {
25
+ "@context": "https://schema.org",
26
+ "@type": "FAQPage",
27
+ "mainEntity": [
28
+ {
29
+ "@type": "Question",
30
+ "name": "How do I test a shell script on Debian, Ubuntu, and Alpine?",
31
+ "acceptedAnswer": {
32
+ "@type": "Answer",
33
+ "text": "Add the OpsScript Gate GitHub Action or install the Python CLI, then run the script in real Debian, Ubuntu, and Alpine containers. The report shows the failing image, exit code, line, and missing command."
34
+ }
35
+ },
36
+ {
37
+ "@type": "Question",
38
+ "name": "Why can ShellCheck pass while a shell installer still fails with command not found?",
39
+ "acceptedAnswer": {
40
+ "@type": "Answer",
41
+ "text": "ShellCheck analyzes shell source and syntax. It does not know which binaries are installed in the runtime image. OpsScript Gate executes the script so missing commands and package-manager assumptions appear in the target distribution."
42
+ }
43
+ },
44
+ {
45
+ "@type": "Question",
46
+ "name": "How do I diagnose apt-get not found on Alpine?",
47
+ "acceptedAnswer": {
48
+ "@type": "Answer",
49
+ "text": "Alpine normally uses apk rather than apt-get. Run the installer across the matrix and branch on the package manager or document a distribution-specific prerequisite."
50
+ }
51
+ },
52
+ {
53
+ "@type": "Question",
54
+ "name": "Can I run the cross-distro shell check without GitHub Actions?",
55
+ "acceptedAnswer": {
56
+ "@type": "Answer",
57
+ "text": "Yes. Install opsscript-gate from PyPI, ensure a Docker engine running Linux containers is reachable, and run opsscript-gate run ./install.sh."
58
+ }
59
+ }
60
+ ]
61
+ }
62
+ </script>
8
63
  <link rel="canonical" href="https://mresyzz.github.io/opsscript-gate/command-not-found.html">
9
64
  <meta property="og:title" content="ShellCheck passed. Does your installer run on Alpine?">
10
65
  <meta property="og:description" content="A reproducible shell runtime compatibility check for Debian, Ubuntu and Alpine, with a copyable GitHub Actions workflow.">
@@ -79,7 +134,7 @@ jobs:
79
134
  - uses: actions/checkout@v7
80
135
  with:
81
136
  persist-credentials: false
82
- - uses: Mresyzz/opsscript-gate@v0.9.0
137
+ - uses: Mresyzz/opsscript-gate@v0.9.2
83
138
  with:
84
139
  script-path: install.sh
85
140
  shell: auto
@@ -113,6 +168,20 @@ opsscript-gate run ./install.sh</code></pre>
113
168
  <p>OpsScript Gate mounts each script independently. Sibling files and repository dependencies are not mounted.
114
169
  These are distribution user-space tests in containers, not separate Linux kernels or full-machine tests.
115
170
  Passing checks do not prove security or replace project integration tests.</p>
171
+
172
+ <h2 id="faq">Frequently asked questions</h2>
173
+ <h3>How do I test a shell script on Debian, Ubuntu, and Alpine?</h3>
174
+ <p>Add the GitHub Action or install the CLI, then run the script in real containers for each distribution.
175
+ The report identifies the failing image, exit code, line, and missing command.</p>
176
+ <h3>Why can ShellCheck pass while the installer still fails?</h3>
177
+ <p>ShellCheck analyzes source and syntax. It cannot know which binaries are installed in the image that
178
+ executes the script. Runtime execution exposes missing commands and package-manager assumptions.</p>
179
+ <h3>How do I diagnose <code>apt-get: not found</code> on Alpine?</h3>
180
+ <p>Alpine normally uses <code>apk</code>. Detect the available package manager or document a distribution-specific
181
+ prerequisite instead of assuming Debian's APT exists everywhere.</p>
182
+ <h3>Can I run the check without GitHub Actions?</h3>
183
+ <p>Yes. Install <code>opsscript-gate</code> from PyPI, make sure a Docker engine running Linux containers is
184
+ reachable, and run <code>opsscript-gate run ./install.sh</code>.</p>
116
185
  <footer>Read the <a href="https://github.com/Mresyzz/opsscript-gate/blob/main/docs/configuration.md">configuration guide</a>,
117
186
  <a href="https://github.com/Mresyzz/opsscript-gate/blob/main/docs/troubleshooting.md">troubleshooting</a>,
118
187
  or <a href="https://github.com/Mresyzz/opsscript-gate/blob/main/SECURITY.md">security boundaries</a>.
@@ -25,7 +25,7 @@ jobs:
25
25
  - uses: actions/checkout@v7
26
26
  with:
27
27
  persist-credentials: false
28
- - uses: Mresyzz/opsscript-gate@v0.9.0
28
+ - uses: Mresyzz/opsscript-gate@v0.9.2
29
29
  with:
30
30
  script-path: scripts/install.sh
31
31
  shell: auto
@@ -117,3 +117,27 @@ run in the Linux environments we support?**
117
117
 
118
118
  See the [Alpine incompatibility example](../examples/alpine-incompatibility/README.md),
119
119
  [troubleshooting guide](troubleshooting.md), or the [interactive demo](https://mresyzz.github.io/opsscript-gate/).
120
+
121
+ ## Frequently asked questions
122
+
123
+ ### How do I test a shell script on Debian, Ubuntu, and Alpine?
124
+
125
+ Add the OpsScript Gate GitHub Action or install the CLI, then run the script in real
126
+ containers for each distribution. The report identifies the failing image, exit code,
127
+ line, and missing command.
128
+
129
+ ### Why can ShellCheck pass while the installer still fails?
130
+
131
+ ShellCheck analyzes source and syntax. It cannot know which binaries are installed in
132
+ the image that executes the script. Runtime execution exposes missing commands and
133
+ package-manager assumptions.
134
+
135
+ ### How do I diagnose `apt-get: not found` on Alpine?
136
+
137
+ Alpine normally uses `apk`. Detect the available package manager or document a
138
+ distribution-specific prerequisite instead of assuming Debian's APT exists everywhere.
139
+
140
+ ### Can I run the check without GitHub Actions?
141
+
142
+ Yes. Install `opsscript-gate` from PyPI, make sure a Docker engine running Linux
143
+ containers is reachable, and run `opsscript-gate run ./install.sh`.
@@ -12,7 +12,7 @@ opsscript-gate run --dry-run
12
12
  ```
13
13
 
14
14
  `init` refuses to overwrite either `.opsscript-gate.json` or
15
- `.github/workflows/opsscript-gate.yml`. The generated workflow uses the v0.9.0 tag.
15
+ `.github/workflows/opsscript-gate.yml`. The generated workflow uses the v0.9.2 tag.
16
16
 
17
17
  `doctor` checks Python and Docker connectivity without executing repository scripts.
18
18
  Use `opsscript-gate doctor --format json` when collecting a support report.
@@ -53,7 +53,7 @@ names to install inside each test container before the target script runs. The
53
53
  runner uses `apt-get` for Debian/Ubuntu images and `apk` for Alpine images. It
54
54
  accepts package names only, never shell commands, and requires `network: bridge`.
55
55
  The default is an empty list, so existing networking settings and execution remain unchanged.
56
- `repeat` runs each distribution more than once and reports `FLAKY` when at least one
56
+ `repeat` runs each distribution more than once (up to 20 attempts) and reports `FLAKY` when at least one
57
57
  attempt passes and another fails. Keep it at `1` for the normal fast gate; use `3` or
58
58
  `5` while investigating intermittent CI failures.
59
59
 
@@ -109,7 +109,7 @@ For pull requests, set `changed-since` to the base commit and fetch full Git his
109
109
  - uses: actions/checkout@v7
110
110
  with:
111
111
  fetch-depth: 0
112
- - uses: Mresyzz/opsscript-gate@v0.9.0
112
+ - uses: Mresyzz/opsscript-gate@v0.9.2
113
113
  with:
114
114
  changed-since: ${{ github.event.pull_request.base.sha }}
115
115
  preset: minimal
@@ -119,7 +119,7 @@ The Action passes when no changed shell scripts are selected. This keeps unrelat
119
119
  documentation or application changes from starting container jobs.
120
120
 
121
121
  ```yaml
122
- - uses: Mresyzz/opsscript-gate@v0.9.0
122
+ - uses: Mresyzz/opsscript-gate@v0.9.2
123
123
  with:
124
124
  config: .opsscript-gate.json
125
125
  format: json
@@ -146,3 +146,6 @@ offline networking and a smaller matrix. Existing users do not need to run `init
146
146
 
147
147
  The intentional breaking corrections are that discovery overflow now fails and
148
148
  symlinked files are no longer automatically selected. Preview discovery after upgrading.
149
+
150
+ `timeout` is the hard limit per container in seconds. It must be between 1 and 3600.
151
+ Custom `matrix` values may contain up to 32 container images.
@@ -33,7 +33,7 @@ jobs:
33
33
  fetch-depth: 0
34
34
  persist-credentials: false
35
35
 
36
- - uses: Mresyzz/opsscript-gate@v0.9.0
36
+ - uses: Mresyzz/opsscript-gate@v0.9.2
37
37
  with:
38
38
  changed-since: ${{ github.event.pull_request.base.sha }}
39
39
  preset: minimal