Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,27 @@ jobs:
- name: Run Pytest
run: |
pytest -v -m "not integration"

integration:
name: Docker Integration Tests
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v7

- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: '3.12'

- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -e .[test]

- name: Verify Docker Available
run: docker version

- name: Run Docker Integration Tests
run: |
pytest -v -m integration
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Add one step to your pull request workflow (`.github/workflows/gate.yml`):
uses: Mresyzz/opsscript-gate@v0.1.2
with:
script-path: scripts/setup.sh
# shell: posix # optional: 'posix' (default), 'shebang', or 'auto'
```

### In Local Terminal (CLI)
Expand Down Expand Up @@ -151,6 +152,7 @@ You can customize the matrix at any time via `--matrix` or Action input `matrix`
```text
usage: opsscript-gate run [-h] [--matrix MATRIX] [--timeout TIMEOUT]
[--format {table,markdown,json}]
[--shell {posix,shebang,auto}]
script_path
```

Expand All @@ -160,9 +162,16 @@ usage: opsscript-gate run [-h] [--matrix MATRIX] [--timeout TIMEOUT]
| `--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` |
| `--shell` | Choice | `posix` | Execution mode: `posix` (default), `shebang`, or `auto` |
| `--version` | Flag | - | Show version number |
| `-h, --help` | Flag | - | Show argument help |

### Shell Execution Modes (`--shell`)

- **`posix`** (default): Strictly executes with `/bin/sh`, ignoring any script shebang. Ideal for verifying that your script runs in minimal POSIX-compliant environments (e.g. Alpine BusyBox).
- **`shebang`**: Strictly honors the interpreter specified in the script's shebang (`#!/bin/sh`, `#!/bin/bash`, `#!/usr/bin/sh`, `#!/usr/bin/bash`, `#!/usr/bin/env sh`, `#!/usr/bin/env bash`). If the shebang is missing, malformed, or specifies an unsupported interpreter/flag, the check fails immediately with an error before running containers.
- **`auto`**: Honors recognized shebangs if present; falls back to `/bin/sh` if no shebang is declared. Scripts with explicit unsupported or malformed shebangs fail immediately with an error (does not silently execute as POSIX).

### Exit Code Convention
- **`0`**: All distributions passed (`PASS`).
- **`1`**: At least one distribution failed (`FAIL`), timed out (`TIMED_OUT`), or errored (`ERROR`).
Expand Down Expand Up @@ -202,7 +211,7 @@ pytest -v
## Current limitations

- Requires access to a Docker daemon.
- Scripts are currently executed with `/bin/sh`, regardless of their shebang.
- Scripts are executed with `/bin/sh` by default; use `--shell shebang` or `--shell auto` for shebang-aware execution.
- Distribution runs are currently sequential.
- Containers use bridge networking by default.
- Failure reports currently include only a tail of captured output.
Expand All @@ -213,7 +222,6 @@ pytest -v
## Roadmap

See [ROADMAP.md](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
Expand Down
4 changes: 2 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ This roadmap tracks features being considered for future releases.
## Planned Features

### 1. Execution Modes (`--shell`)
- [ ] **Shebang-aware execution (`--shell shebang | auto`)**: Under consideration. Parse and honor `#!/usr/bin/env bash` or `#!/bin/bash` in distributions where Bash is present, while maintaining unprivileged container defaults.
- [ ] **Explicit POSIX mode (`--shell posix`)**: Retain the current baseline of `/bin/sh` across all containers to verify portability.
- [x] **Shebang-aware execution (`--shell shebang | auto`)**: Implemented on branch `feat/shell-mode` (upcoming release). Parses and honors recognized shebang forms (`sh`, `bash`) across distributions using fixed trusted container commands.
- [x] **Explicit POSIX mode (`--shell posix`)**: Implemented on branch `feat/shell-mode` (upcoming release). Retains `/bin/sh` baseline across all containers to verify strict POSIX portability.

### 2. Resource Constraints
- [ ] **Memory limits (`--mem-limit`)**: Planned. Constrain container memory usage for scripts under test.
Expand Down
22 changes: 19 additions & 3 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ inputs:
description: 'Output report format: table, markdown, or json'
required: false
default: 'table'
shell:
description: "Shell execution mode: 'posix' (default), 'shebang', or 'auto'"
required: false
default: 'posix'

runs:
using: 'composite'
Expand All @@ -38,9 +42,21 @@ runs:

- name: Run compatibility gate
shell: bash
env:
INPUT_SCRIPT_PATH: ${{ inputs.script-path }}
INPUT_MATRIX: ${{ inputs.matrix }}
INPUT_TIMEOUT: ${{ inputs.timeout }}
INPUT_FORMAT: ${{ inputs.format }}
INPUT_SHELL: ${{ inputs.shell }}
run: |
ARGS=("run" "${{ inputs.script-path }}" "--timeout" "${{ inputs.timeout }}" "--format" "${{ inputs.format }}")
if [ -n "${{ inputs.matrix }}" ]; then
ARGS+=("--matrix" "${{ inputs.matrix }}")
ARGS=(
"run"
"$INPUT_SCRIPT_PATH"
"--timeout" "$INPUT_TIMEOUT"
"--format" "$INPUT_FORMAT"
"--shell" "$INPUT_SHELL"
)
if [ -n "$INPUT_MATRIX" ]; then
ARGS+=("--matrix" "$INPUT_MATRIX")
fi
python -m opsscript_gate.cli "${ARGS[@]}"
7 changes: 7 additions & 0 deletions src/opsscript_gate/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ def build_parser() -> argparse.ArgumentParser:
default="table",
help="Output report format: 'table' (default ASCII), 'markdown', or 'json'",
)
run_parser.add_argument(
"--shell",
choices=["posix", "shebang", "auto"],
default="posix",
help="Shell execution mode: 'posix' (default, /bin/sh), 'shebang' (honors script shebang), or 'auto' (shebang if recognized, else /bin/sh)",
)

return parser

Expand Down Expand Up @@ -98,6 +104,7 @@ def main(argv: list[str] | None = None) -> int:
script_path=script_path,
matrix=matrix,
timeout=args.timeout,
shell_mode=args.shell,
)
except DockerDaemonError as err:
sys.stderr.write(f"Docker Error: {err}\n")
Expand Down
7 changes: 7 additions & 0 deletions src/opsscript_gate/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ class DistroStatus(str, Enum):
ERROR = "ERROR"


class ShellMode(str, Enum):
"""Execution mode for shell scripts."""
POSIX = "posix"
SHEBANG = "shebang"
AUTO = "auto"


@dataclass
class SingleResult:
"""Execution result for a single Linux distribution."""
Expand Down
Loading
Loading