Skip to content

Commit 1e40451

Browse files
authored
Merge pull request #2 from Mresyzz/feat/shell-mode
feat: add explicit shell execution modes
2 parents 8cb5e82 + 6df13d4 commit 1e40451

8 files changed

Lines changed: 737 additions & 16 deletions

File tree

‎.github/workflows/test.yml‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,27 @@ jobs:
2626
- name: Run Pytest
2727
run: |
2828
pytest -v -m "not integration"
29+
30+
integration:
31+
name: Docker Integration Tests
32+
runs-on: ubuntu-latest
33+
steps:
34+
- name: Checkout Code
35+
uses: actions/checkout@v7
36+
37+
- name: Set up Python
38+
uses: actions/setup-python@v7
39+
with:
40+
python-version: '3.12'
41+
42+
- name: Install Dependencies
43+
run: |
44+
python -m pip install --upgrade pip
45+
pip install -e .[test]
46+
47+
- name: Verify Docker Available
48+
run: docker version
49+
50+
- name: Run Docker Integration Tests
51+
run: |
52+
pytest -v -m integration

‎README.md‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Add one step to your pull request workflow (`.github/workflows/gate.yml`):
3030
uses: Mresyzz/opsscript-gate@v0.1.2
3131
with:
3232
script-path: scripts/setup.sh
33+
# shell: posix # optional: 'posix' (default), 'shebang', or 'auto'
3334
```
3435

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

@@ -160,9 +162,16 @@ usage: opsscript-gate run [-h] [--matrix MATRIX] [--timeout TIMEOUT]
160162
| `--matrix` | String | `debian:12-slim,ubuntu:22.04,ubuntu:24.04,alpine:3.20` | Comma-separated list of Docker images |
161163
| `--timeout` | Integer | `60` | Hard timeout per container in seconds |
162164
| `--format` | Choice | `table` | Output format: `table`, `markdown`, or `json` |
165+
| `--shell` | Choice | `posix` | Execution mode: `posix` (default), `shebang`, or `auto` |
163166
| `--version` | Flag | - | Show version number |
164167
| `-h, --help` | Flag | - | Show argument help |
165168

169+
### Shell Execution Modes (`--shell`)
170+
171+
- **`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).
172+
- **`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.
173+
- **`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).
174+
166175
### Exit Code Convention
167176
- **`0`**: All distributions passed (`PASS`).
168177
- **`1`**: At least one distribution failed (`FAIL`), timed out (`TIMED_OUT`), or errored (`ERROR`).
@@ -202,7 +211,7 @@ pytest -v
202211
## Current limitations
203212

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

215224
See [ROADMAP.md](ROADMAP.md) for planned capabilities, including:
216-
- Shebang-aware execution modes (`--shell auto|posix|shebang`)
217225
- Container resource limits (`--mem-limit`, `--pids-limit`)
218226
- Configurable network isolation (`--network none|bridge`)
219227
- Parallel matrix execution

‎ROADMAP.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ This roadmap tracks features being considered for future releases.
99
## Planned Features
1010

1111
### 1. Execution Modes (`--shell`)
12-
- [ ] **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.
13-
- [ ] **Explicit POSIX mode (`--shell posix`)**: Retain the current baseline of `/bin/sh` across all containers to verify portability.
12+
- [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.
13+
- [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.
1414

1515
### 2. Resource Constraints
1616
- [ ] **Memory limits (`--mem-limit`)**: Planned. Constrain container memory usage for scripts under test.

‎action.yml‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ inputs:
2121
description: 'Output report format: table, markdown, or json'
2222
required: false
2323
default: 'table'
24+
shell:
25+
description: "Shell execution mode: 'posix' (default), 'shebang', or 'auto'"
26+
required: false
27+
default: 'posix'
2428

2529
runs:
2630
using: 'composite'
@@ -38,9 +42,21 @@ runs:
3842
3943
- name: Run compatibility gate
4044
shell: bash
45+
env:
46+
INPUT_SCRIPT_PATH: ${{ inputs.script-path }}
47+
INPUT_MATRIX: ${{ inputs.matrix }}
48+
INPUT_TIMEOUT: ${{ inputs.timeout }}
49+
INPUT_FORMAT: ${{ inputs.format }}
50+
INPUT_SHELL: ${{ inputs.shell }}
4151
run: |
42-
ARGS=("run" "${{ inputs.script-path }}" "--timeout" "${{ inputs.timeout }}" "--format" "${{ inputs.format }}")
43-
if [ -n "${{ inputs.matrix }}" ]; then
44-
ARGS+=("--matrix" "${{ inputs.matrix }}")
52+
ARGS=(
53+
"run"
54+
"$INPUT_SCRIPT_PATH"
55+
"--timeout" "$INPUT_TIMEOUT"
56+
"--format" "$INPUT_FORMAT"
57+
"--shell" "$INPUT_SHELL"
58+
)
59+
if [ -n "$INPUT_MATRIX" ]; then
60+
ARGS+=("--matrix" "$INPUT_MATRIX")
4561
fi
4662
python -m opsscript_gate.cli "${ARGS[@]}"

‎src/opsscript_gate/cli.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ def build_parser() -> argparse.ArgumentParser:
6464
default="table",
6565
help="Output report format: 'table' (default ASCII), 'markdown', or 'json'",
6666
)
67+
run_parser.add_argument(
68+
"--shell",
69+
choices=["posix", "shebang", "auto"],
70+
default="posix",
71+
help="Shell execution mode: 'posix' (default, /bin/sh), 'shebang' (honors script shebang), or 'auto' (shebang if recognized, else /bin/sh)",
72+
)
6773

6874
return parser
6975

@@ -98,6 +104,7 @@ def main(argv: list[str] | None = None) -> int:
98104
script_path=script_path,
99105
matrix=matrix,
100106
timeout=args.timeout,
107+
shell_mode=args.shell,
101108
)
102109
except DockerDaemonError as err:
103110
sys.stderr.write(f"Docker Error: {err}\n")

‎src/opsscript_gate/models.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ class DistroStatus(str, Enum):
1313
ERROR = "ERROR"
1414

1515

16+
class ShellMode(str, Enum):
17+
"""Execution mode for shell scripts."""
18+
POSIX = "posix"
19+
SHEBANG = "shebang"
20+
AUTO = "auto"
21+
22+
1623
@dataclass
1724
class SingleResult:
1825
"""Execution result for a single Linux distribution."""

0 commit comments

Comments
 (0)