Skip to main content

OpsScript Gate

ShellCheck tells you if your script looks portable. OpsScript Gate checks if it actually runs there.

CI Demo GitHub Marketplace Release Python Version License: MIT Supported Distros

OpsScript Gate is a drop-in runtime compatibility gate for Linux shell scripts. It executes your shell scripts inside isolated Debian, Ubuntu, and Alpine containers before release, catching environment-specific runtime failures that static analysis cannot detect.

OpsScript Gate Terminal Preview


Quickstart

In GitHub Actions

Add one step to your pull request workflow (.github/workflows/gate.yml):

- name: Verify Shell Script Portability
  uses: Mresyzz/opsscript-gate@v0.1.1
  with:
    script-path: scripts/setup.sh

In Local Terminal (CLI)

Requires Python 3.10+ and a local Docker engine:

# Install directly from GitHub
pip install git+https://github.com/Mresyzz/opsscript-gate.git

# Run compatibility gate against your script
opsscript-gate run ./scripts/setup.sh

Example: What Static Analysis Misses

Consider this deployment script:

#!/bin/sh
set -e
echo "Fetching package information..."
apt-get --version

Running shellcheck reports 0 errors, 0 warnings because the syntax is valid POSIX shell.

However, when verified with OpsScript Gate:

+--------------------+----------+-----------+----------------------------------------------------+
| Distro             | Status   | Exit Code | Details                                            |
+--------------------+----------+-----------+----------------------------------------------------+
| debian:12-slim     | PASS     | 0         | OK                                                 |
| ubuntu:22.04       | PASS     | 0         | OK                                                 |
| ubuntu:24.04       | PASS     | 0         | OK                                                 |
| alpine:3.20        | FAIL     | 127       | Script failed with non-zero exit code: 127         |
+--------------------+----------+-----------+----------------------------------------------------+
Result: FAILED

============================================================
Failed Distributions - Output Snippets (last 15 lines):
============================================================

--- [alpine:3.20] (FAIL) ---
/tmp/target_script.sh: line 4: apt-get: not found

Example output; timing values omitted because they vary by host and image cache state.

Why it failed: Alpine Linux is musl/BusyBox-based and uses apk, not apt-get. OpsScript Gate catches the missing utility (exit code 127) during test execution, before the script is deployed.


Why OpsScript Gate?

OpsScript Gate vs ShellCheck vs Custom CI Matrix

Capability OpsScript Gate ShellCheck Handwritten CI Matrix
Runtime execution Yes No (Static AST only) Yes
Real distro environments Yes (Debian, Ubuntu, Alpine) No Yes
Preconfigured defaults Yes Yes Requires custom workflow configuration
Safe container defaults Built-in (ro, cap_drop, kill) N/A User-defined
Anti-hang stdin protection Built-in (</dev/null, noninteractive) No User-defined
Unified summary & diagnostics Built-in (ASCII + Step Summary) Static warnings User-defined
  • ShellCheck is indispensable for static analysis (syntax, quoting, SC warnings). OpsScript Gate complements it by testing actual execution behavior in real distributions.
  • Handwritten CI Matrix requires maintaining complex Docker configurations, volume mounts, timeout guards, and log parsers across every project. OpsScript Gate packages this into a single check.

Security Boundaries

OpsScript Gate uses conservative container defaults when running scripts:

  1. Unprivileged by Design:
    • Containers run with privileged=False.
    • All Linux capabilities are dropped: cap_drop=["ALL"].
    • Privilege escalation is disabled: security_opt=["no-new-privileges:true"].
  2. Read-Only Target Mount:
    • The tested script is mounted read-only (:ro) at /tmp/target_script.sh.
    • OpsScript Gate does not mount additional host filesystem paths into the test container.
  3. Anti-Hang Deadlock Defense:
    • Disables TTY and stdin (stdin_open=False, tty=False).
    • Redirects execution: /bin/sh -c "/bin/sh /tmp/target_script.sh </dev/null".
    • Injects DEBIAN_FRONTEND=noninteractive and CI=true.
    • Any script prompting for user input (read -p) fails immediately instead of blocking the CI runner.
  4. Timeout & Container Cleanup:
    • Enforces a configurable timeout (default: 60s). Timed-out containers are sent SIGKILL and marked TIMED_OUT.
    • Container removal is attempted from a finally block during normal Python execution paths, including failures and timeouts.
  5. Windows CRLF Defense:
    • Automatically detects and normalizes carriage returns (\r\n -> \n) before container execution, preventing false \r: command not found errors.
  6. POSIX-oriented /bin/sh Baseline:
    • Containers invoke /bin/sh directly, catching undeclared Bashism syntax (e.g. bash arrays, [[ ... ]]) that break in lightweight Alpine environments.

Default Test Matrix

Image Distribution Focus
debian:12-slim Debian 12 (Bookworm) Minimal glibc + APT base
ubuntu:22.04 Ubuntu 22.04 LTS (Jammy) Enterprise long-term support baseline
ubuntu:24.04 Ubuntu 24.04 LTS (Noble) Modern glibc, updated coreutils & defaults
alpine:3.20 Alpine Linux 3.20 Minimal musl libc + BusyBox (strict POSIX test)

You can customize the matrix at any time via --matrix or Action input matrix.


CLI Reference

usage: opsscript-gate run [-h] [--matrix MATRIX] [--timeout TIMEOUT]
                          [--format {table,markdown,json}]
                          script_path
Parameter Type Default Description
script_path Positional Required Path to target shell script
--matrix String debian:12-slim,ubuntu:22.04,ubuntu:24.04,alpine:3.20 Comma-separated list of Docker images
--timeout Integer 60 Hard timeout per container in seconds
--format Choice table Output format: table, markdown, or json
--version Flag - Show version number
-h, --help Flag - Show argument help

Exit Code Convention

  • 0: All distributions passed (PASS).
  • 1: At least one distribution failed (FAIL), timed out (TIMED_OUT), or errored (ERROR).

Examples

Check out the examples/ directory for self-contained, runnable scenarios:


Development & Testing

The test suite uses Docker SDK mocking to ensure fast unit tests without needing a local daemon:

# Clone and install with test dependencies
git clone https://github.com/Mresyzz/opsscript-gate.git
cd opsscript-gate
pip install -e .[test]

# Run unit tests (mocked)
pytest -v -m "not integration"

# Run integration tests (Requires Docker daemon)
pytest -v

Roadmap

See ROADMAP.md for planned capabilities, including:

  • Shebang-aware execution modes (--shell auto|posix|shebang)
  • Container resource limits (--mem-limit, --pids-limit)
  • Configurable network isolation (--network none|bridge)
  • Parallel matrix execution

Contributing & Security

  • Contributing: Please review CONTRIBUTING.md for pull request guidelines and security boundaries.
  • Security Policy: Read SECURITY.md to report vulnerabilities responsibly.
  • Changelog: See CHANGELOG.md for release history.

License

OpsScript Gate is licensed under the MIT License.

Release files for opsscript-gate 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for opsscript-gate 0.1.2
File Size Uploaded
opsscript_gate-0.1.2.tar.gz 76.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for opsscript-gate 0.1.2
File Interpreter ABI Platform
opsscript_gate-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 89.6 kB

Release files / opsscript_gate-0.1.2.tar.gz

Download URL opsscript_gate-0.1.2.tar.gz
Size 76.1 kB
Tags Source
SHA-256 checksum
How to use checksums
cbcb9003007242cc2a7fd93e01bdc0c4b6f93b16b60a7324a110a014359e8271
BLAKE2b-256 checksum
How to use checksums
b74596b33c9cc116efd40c9261ad7fd8cb25cf1c8709734d0e8c942e35f89236
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release files / opsscript_gate-0.1.2-py3-none-any.whl

Download URL opsscript_gate-0.1.2-py3-none-any.whl
Size 13.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3503770a37346b13e3dd36dbdcbe068762e55cbd0c941f9e0ccdb0ad2e4a4342
BLAKE2b-256 checksum
How to use checksums
a912ddf5470d286d02015de7dc44492c9ac164a408c913433234da341bf12b9c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

This release

0.1.2 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page