opsscript-gate 0.6.2__tar.gz → 0.6.3__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 (48) hide show
  1. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/.github/workflow-templates/opsscript-gate.yml +1 -1
  2. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/CHANGELOG.md +9 -0
  3. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/CITATION.cff +2 -2
  4. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/PKG-INFO +14 -6
  5. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/README.md +12 -4
  6. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/README.zh-CN.md +9 -4
  7. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/action.yml +1 -1
  8. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/docs/configuration.md +7 -3
  9. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/docs/gpt-oss.md +1 -1
  10. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/examples/github-actions/README.md +1 -1
  11. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/examples/github-actions/workflow.yml +1 -1
  12. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/pyproject.toml +5 -1
  13. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/src/opsscript_gate/__init__.py +1 -1
  14. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/src/opsscript_gate/cli.py +43 -0
  15. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/src/opsscript_gate/config.py +1 -1
  16. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/tests/test_onboarding.py +22 -0
  17. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/.gitattributes +0 -0
  18. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  19. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/.github/ISSUE_TEMPLATE/distro_request.yml +0 -0
  20. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  21. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/.github/assets/social-preview.png +0 -0
  22. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/.github/pull_request_template.md +0 -0
  23. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/.github/workflow-templates/properties.json +0 -0
  24. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/.github/workflows/demo.yml +0 -0
  25. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/.github/workflows/release.yml +0 -0
  26. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/.github/workflows/test.yml +0 -0
  27. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/.gitignore +0 -0
  28. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/CONTRIBUTING.md +0 -0
  29. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/LICENSE +0 -0
  30. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/ROADMAP.md +0 -0
  31. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/SECURITY.md +0 -0
  32. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/docs/troubleshooting.md +0 -0
  33. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/examples/alpine-incompatibility/README.md +0 -0
  34. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/examples/alpine-incompatibility/apt_dependency.sh +0 -0
  35. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/examples/basic/README.md +0 -0
  36. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/examples/basic/clean_setup.sh +0 -0
  37. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/examples/interactive-hang/README.md +0 -0
  38. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/examples/interactive-hang/prompt_confirm.sh +0 -0
  39. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/src/opsscript_gate/discovery.py +0 -0
  40. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/src/opsscript_gate/models.py +0 -0
  41. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/src/opsscript_gate/remediation.py +0 -0
  42. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/src/opsscript_gate/reporter.py +0 -0
  43. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/src/opsscript_gate/runner.py +0 -0
  44. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/tests/fixtures/fail_deps.sh +0 -0
  45. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/tests/fixtures/fail_hang.sh +0 -0
  46. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/tests/fixtures/pass_basic.sh +0 -0
  47. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/tests/test_action_contract.py +0 -0
  48. {opsscript_gate-0.6.2 → opsscript_gate-0.6.3}/tests/test_runner.py +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.6.2
28
+ uses: Mresyzz/opsscript-gate@v0.6.3
29
29
  with:
30
30
  shell: auto
31
31
  network: none
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.6.3] - 2026-10-01
4
+
5
+ ### Added
6
+ - Added `opsscript-gate doctor` to diagnose Python and Docker prerequisites before a local run.
7
+
8
+ ### Changed
9
+ - Expanded package and Marketplace keywords for shell installers, ShellCheck users, portability, and BusyBox.
10
+ - Clarified the Action description around shell installer runtime compatibility.
11
+
3
12
  ## [0.6.2] - 2026-10-01
4
13
 
5
14
  ### Changed
@@ -5,8 +5,8 @@ type: software
5
5
  authors:
6
6
  - family-names: Mresyzz
7
7
  given-names: Mresy
8
- version: 0.6.2
9
- date-released: 2026-09-30
8
+ version: 0.6.3
9
+ date-released: 2026-10-01
10
10
  repository-code: https://github.com/Mresyzz/opsscript-gate
11
11
  url: https://github.com/Mresyzz/opsscript-gate
12
12
  license: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: opsscript-gate
3
- Version: 0.6.2
3
+ Version: 0.6.3
4
4
  Summary: Run Linux shell scripts in Debian, Ubuntu and Alpine containers to catch runtime compatibility failures before merge
5
5
  Project-URL: Homepage, https://mresyzz.github.io/InstallReady/
6
6
  Project-URL: Demo, https://mresyzz.github.io/InstallReady/?repo=Mresyzz%2Fopsscript-gate
@@ -11,7 +11,7 @@ Project-URL: Documentation, https://github.com/Mresyzz/opsscript-gate/tree/main/
11
11
  Author: Mresyzz
12
12
  License: MIT
13
13
  License-File: LICENSE
14
- Keywords: alpine,bootstrap,ci,compatibility,cross-distro,debian,devops,docker,github-actions,installer,linux,posix,shell,shellcheck-alternative,testing,ubuntu
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
15
  Classifier: Development Status :: 4 - Beta
16
16
  Classifier: Intended Audience :: Developers
17
17
  Classifier: Intended Audience :: System Administrators
@@ -56,7 +56,7 @@ Try the browser scanner: [InstallReady](https://mresyzz.github.io/InstallReady/?
56
56
 
57
57
  ---
58
58
 
59
- ## Current release: v0.6.2
59
+ ## Current release: v0.6.3
60
60
 
61
61
  The current release includes a project config file, a dry-run plan, presets, exclusions,
62
62
  saved reports, and changed-script selection for pull requests. The project controls
@@ -64,6 +64,7 @@ were introduced in v0.5.0; projects on v0.4.1 and earlier do not include them.
64
64
 
65
65
  ```bash
66
66
  opsscript-gate init
67
+ opsscript-gate doctor
67
68
  opsscript-gate run --dry-run
68
69
  opsscript-gate run --format json --output reports/compatibility.json
69
70
  ```
@@ -71,6 +72,7 @@ opsscript-gate run --format json --output reports/compatibility.json
71
72
  `init` creates `.opsscript-gate.json` and a GitHub Actions workflow without replacing
72
73
  existing files. Review its exclusions (`tests/*`, `examples/*`) and offline network
73
74
  default before running. No Docker is required for `init` or `--dry-run`.
75
+ Use `opsscript-gate doctor` to check Python and Docker before a real run.
74
76
 
75
77
  Useful for standalone installers, container entrypoints and release scripts that
76
78
  must work on both GNU/Linux and Alpine/BusyBox. Each script runs independently;
@@ -99,7 +101,7 @@ jobs:
99
101
  runs-on: ubuntu-latest
100
102
  steps:
101
103
  - uses: actions/checkout@v7
102
- - uses: Mresyzz/opsscript-gate@v0.6.2
104
+ - uses: Mresyzz/opsscript-gate@v0.6.3
103
105
  ```
104
106
 
105
107
  This zero-configuration form discovers shell scripts in the repository. Use
@@ -110,7 +112,7 @@ repository has a single installer. For self-hosted model installers, see the
110
112
  Or test a specific script with custom execution modes:
111
113
 
112
114
  ```yaml
113
- - uses: Mresyzz/opsscript-gate@v0.6.2
115
+ - uses: Mresyzz/opsscript-gate@v0.6.3
114
116
  with:
115
117
  script-path: scripts/install.sh
116
118
  shell: auto
@@ -141,7 +143,7 @@ unrelated scripts:
141
143
  - uses: actions/checkout@v7
142
144
  with:
143
145
  fetch-depth: 0
144
- - uses: Mresyzz/opsscript-gate@v0.6.2
146
+ - uses: Mresyzz/opsscript-gate@v0.6.3
145
147
  with:
146
148
  changed-since: ${{ github.event.pull_request.base.sha }}
147
149
  preset: minimal
@@ -353,6 +355,12 @@ Customize the matrix at any time via `--matrix` or Action input `matrix`.
353
355
 
354
356
  ## CLI reference
355
357
 
358
+ Check local prerequisites before starting containers:
359
+
360
+ ```text
361
+ opsscript-gate doctor [--format table|json]
362
+ ```
363
+
356
364
  ```text
357
365
  usage: opsscript-gate run [-h] [--matrix MATRIX] [-j JOBS] [--timeout TIMEOUT]
358
366
  [--format {table,markdown,json}]
@@ -26,7 +26,7 @@ Try the browser scanner: [InstallReady](https://mresyzz.github.io/InstallReady/?
26
26
 
27
27
  ---
28
28
 
29
- ## Current release: v0.6.2
29
+ ## Current release: v0.6.3
30
30
 
31
31
  The current release includes a project config file, a dry-run plan, presets, exclusions,
32
32
  saved reports, and changed-script selection for pull requests. The project controls
@@ -34,6 +34,7 @@ were introduced in v0.5.0; projects on v0.4.1 and earlier do not include them.
34
34
 
35
35
  ```bash
36
36
  opsscript-gate init
37
+ opsscript-gate doctor
37
38
  opsscript-gate run --dry-run
38
39
  opsscript-gate run --format json --output reports/compatibility.json
39
40
  ```
@@ -41,6 +42,7 @@ opsscript-gate run --format json --output reports/compatibility.json
41
42
  `init` creates `.opsscript-gate.json` and a GitHub Actions workflow without replacing
42
43
  existing files. Review its exclusions (`tests/*`, `examples/*`) and offline network
43
44
  default before running. No Docker is required for `init` or `--dry-run`.
45
+ Use `opsscript-gate doctor` to check Python and Docker before a real run.
44
46
 
45
47
  Useful for standalone installers, container entrypoints and release scripts that
46
48
  must work on both GNU/Linux and Alpine/BusyBox. Each script runs independently;
@@ -69,7 +71,7 @@ jobs:
69
71
  runs-on: ubuntu-latest
70
72
  steps:
71
73
  - uses: actions/checkout@v7
72
- - uses: Mresyzz/opsscript-gate@v0.6.2
74
+ - uses: Mresyzz/opsscript-gate@v0.6.3
73
75
  ```
74
76
 
75
77
  This zero-configuration form discovers shell scripts in the repository. Use
@@ -80,7 +82,7 @@ repository has a single installer. For self-hosted model installers, see the
80
82
  Or test a specific script with custom execution modes:
81
83
 
82
84
  ```yaml
83
- - uses: Mresyzz/opsscript-gate@v0.6.2
85
+ - uses: Mresyzz/opsscript-gate@v0.6.3
84
86
  with:
85
87
  script-path: scripts/install.sh
86
88
  shell: auto
@@ -111,7 +113,7 @@ unrelated scripts:
111
113
  - uses: actions/checkout@v7
112
114
  with:
113
115
  fetch-depth: 0
114
- - uses: Mresyzz/opsscript-gate@v0.6.2
116
+ - uses: Mresyzz/opsscript-gate@v0.6.3
115
117
  with:
116
118
  changed-since: ${{ github.event.pull_request.base.sha }}
117
119
  preset: minimal
@@ -323,6 +325,12 @@ Customize the matrix at any time via `--matrix` or Action input `matrix`.
323
325
 
324
326
  ## CLI reference
325
327
 
328
+ Check local prerequisites before starting containers:
329
+
330
+ ```text
331
+ opsscript-gate doctor [--format table|json]
332
+ ```
333
+
326
334
  ```text
327
335
  usage: opsscript-gate run [-h] [--matrix MATRIX] [-j JOBS] [--timeout TIMEOUT]
328
336
  [--format {table,markdown,json}]
@@ -10,9 +10,9 @@ Bash 依赖、包管理器假设和交互式阻塞。它与 ShellCheck 互补。
10
10
 
11
11
  也可以直接打开 [InstallReady 在线扫描器](https://mresyzz.github.io/InstallReady/?repo=Mresyzz%2Fopsscript-gate),无需登录即可检查公开仓库。
12
12
 
13
- ## 当前版本 v0.6.2
13
+ ## 当前版本 v0.6.3
14
14
 
15
- v0.6.2 已作为正式版本发布;如果你要从源码验证当前分支,也可以执行:
15
+ v0.6.3 已作为正式版本发布;如果你要从源码验证当前分支,也可以执行:
16
16
 
17
17
  ```bash
18
18
  pip install -e .
@@ -23,13 +23,15 @@ opsscript-gate run examples/basic/clean_setup.sh --dry-run
23
23
 
24
24
  ```bash
25
25
  opsscript-gate init
26
+ opsscript-gate doctor
26
27
  opsscript-gate run --dry-run
27
28
  opsscript-gate run --format json --output reports/compatibility.json
28
29
  ```
29
30
 
30
- `init` 生成配置和 GitHub 工作流,不覆盖已有文件。工作流引用 `v0.6.2`。
31
+ `init` 生成配置和 GitHub 工作流,不覆盖已有文件。工作流引用 `v0.6.3`。
31
32
  预览不需要 Docker;真实运行需要 Python 3.10+
32
33
  和可访问的 Linux Docker 引擎。
34
+ `opsscript-gate doctor` 可以在真实运行前检查 Python 和 Docker。
33
35
 
34
36
  生成的配置默认排除 `tests/*`、`examples/*`,使用 Debian + Alpine,尊重
35
37
  脚本 shebang,并关闭网络。请根据实际脚本审核配置;需要下载文件时启用
@@ -47,13 +49,16 @@ opsscript-gate run --format json --output reports/compatibility.json
47
49
  返回码 `0` 表示全部通过,`1` 表示检查失败或发生错误。`--dry-run` 成功只表示
48
50
  执行计划生成成功。容器不是不可信代码的安全沙箱。
49
51
 
52
+ 需要排查本地环境时运行 `opsscript-gate doctor --format json`;它会检查 Python、
53
+ Docker daemon 和可选的项目配置。
54
+
50
55
  在 Pull Request 中只检查本次修改的脚本,可以先获取完整 Git 历史,再传入目标分支提交:
51
56
 
52
57
  ```yaml
53
58
  - uses: actions/checkout@v7
54
59
  with:
55
60
  fetch-depth: 0
56
- - uses: Mresyzz/opsscript-gate@v0.6.2
61
+ - uses: Mresyzz/opsscript-gate@v0.6.3
57
62
  with:
58
63
  changed-since: ${{ github.event.pull_request.base.sha }}
59
64
  preset: minimal
@@ -1,5 +1,5 @@
1
1
  name: 'OpsScript Gate'
2
- description: 'Run Linux shell scripts in Debian, Ubuntu and Alpine containers to catch runtime compatibility failures before merge'
2
+ description: 'Run Linux shell installers across Debian, Ubuntu, and Alpine; a ShellCheck companion for runtime compatibility in CI.'
3
3
  author: 'Mresyzz'
4
4
  branding:
5
5
  icon: 'shield'
@@ -7,11 +7,15 @@ have these commands.
7
7
 
8
8
  ```bash
9
9
  opsscript-gate init
10
+ opsscript-gate doctor
10
11
  opsscript-gate run --dry-run
11
12
  ```
12
13
 
13
14
  `init` refuses to overwrite either `.opsscript-gate.json` or
14
- `.github/workflows/opsscript-gate.yml`. The generated workflow uses the v0.6.2 tag.
15
+ `.github/workflows/opsscript-gate.yml`. The generated workflow uses the v0.6.3 tag.
16
+
17
+ `doctor` checks Python and Docker connectivity without executing repository scripts.
18
+ Use `opsscript-gate doctor --format json` when collecting a support report.
15
19
 
16
20
  The generated config uses the two-image `minimal` preset, honors recognized shell
17
21
  shebangs, disables networking, and excludes `tests/*` and `examples/*`. Review these
@@ -95,7 +99,7 @@ For pull requests, set `changed-since` to the base commit and fetch full Git his
95
99
  - uses: actions/checkout@v7
96
100
  with:
97
101
  fetch-depth: 0
98
- - uses: Mresyzz/opsscript-gate@v0.6.2
102
+ - uses: Mresyzz/opsscript-gate@v0.6.3
99
103
  with:
100
104
  changed-since: ${{ github.event.pull_request.base.sha }}
101
105
  preset: minimal
@@ -105,7 +109,7 @@ The Action passes when no changed shell scripts are selected. This keeps unrelat
105
109
  documentation or application changes from starting container jobs.
106
110
 
107
111
  ```yaml
108
- - uses: Mresyzz/opsscript-gate@v0.6.2
112
+ - uses: Mresyzz/opsscript-gate@v0.6.3
109
113
  with:
110
114
  config: .opsscript-gate.json
111
115
  format: json
@@ -33,7 +33,7 @@ jobs:
33
33
  fetch-depth: 0
34
34
  persist-credentials: false
35
35
 
36
- - uses: Mresyzz/opsscript-gate@v0.6.2
36
+ - uses: Mresyzz/opsscript-gate@v0.6.3
37
37
  with:
38
38
  changed-since: ${{ github.event.pull_request.base.sha }}
39
39
  preset: minimal
@@ -2,4 +2,4 @@
2
2
 
3
3
  This example shows a GitHub Actions workflow for checking a shell script on pull requests.
4
4
 
5
- Copy `workflow.yml` into your repository at `.github/workflows/script-gate.yml` and point `script-path` to the script you want to validate. The example uses the current v0.6.2 Action release.
5
+ Copy `workflow.yml` into your repository at `.github/workflows/script-gate.yml` and point `script-path` to the script you want to validate. The example uses the current v0.6.3 Action release.
@@ -19,7 +19,7 @@ jobs:
19
19
  persist-credentials: false
20
20
 
21
21
  - name: Run OpsScript Gate
22
- uses: Mresyzz/opsscript-gate@v0.6.2
22
+ uses: Mresyzz/opsscript-gate@v0.6.3
23
23
  with:
24
24
  script-path: 'scripts/deploy.sh'
25
25
  shell: auto
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "opsscript-gate"
7
- version = "0.6.2"
7
+ version = "0.6.3"
8
8
  description = "Run Linux shell scripts in Debian, Ubuntu and Alpine containers to catch runtime compatibility failures before merge"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -29,6 +29,10 @@ keywords = [
29
29
  "ubuntu",
30
30
  "alpine",
31
31
  "shellcheck-alternative",
32
+ "shellcheck",
33
+ "shell-portability",
34
+ "installer-testing",
35
+ "busybox",
32
36
  ]
33
37
  classifiers = [
34
38
  "Development Status :: 4 - Beta",
@@ -1,4 +1,4 @@
1
1
  """OpsScript Gate: Lightweight cross-distro compatibility pre-check for Linux ops scripts."""
2
2
 
3
- __version__ = "0.6.2"
3
+ __version__ = "0.6.3"
4
4
  __all__ = ["__version__"]
@@ -24,6 +24,7 @@ from opsscript_gate.runner import (
24
24
  DEFAULT_MATRIX,
25
25
  DEFAULT_TIMEOUT,
26
26
  DockerDaemonError,
27
+ get_docker_client,
27
28
  run_matrix,
28
29
  )
29
30
 
@@ -43,6 +44,16 @@ def build_parser() -> argparse.ArgumentParser:
43
44
 
44
45
  subparsers = parser.add_subparsers(dest="command", help="Available subcommands")
45
46
  subparsers.add_parser("init", help="Create project configuration and GitHub workflow")
47
+ doctor_parser = subparsers.add_parser(
48
+ "doctor",
49
+ help="Check the local environment before running containers",
50
+ )
51
+ doctor_parser.add_argument(
52
+ "--format",
53
+ choices=["table", "json"],
54
+ default="table",
55
+ help="Output format: 'table' (default) or 'json'",
56
+ )
46
57
 
47
58
  # 'run' subcommand
48
59
  run_parser = subparsers.add_parser(
@@ -149,6 +160,38 @@ def main(argv: list[str] | None = None) -> int:
149
160
  sys.stderr.write(f"Error: {err}\n")
150
161
  return 1
151
162
 
163
+ if args.command == "doctor":
164
+ checks = [
165
+ {
166
+ "name": "python",
167
+ "ok": sys.version_info >= (3, 10),
168
+ "detail": f"Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
169
+ }
170
+ ]
171
+ try:
172
+ get_docker_client()
173
+ checks.append({"name": "docker", "ok": True, "detail": "Docker daemon is reachable"})
174
+ except DockerDaemonError as err:
175
+ checks.append({"name": "docker", "ok": False, "detail": str(err)})
176
+
177
+ config_path = Path(".opsscript-gate.json")
178
+ checks.append({
179
+ "name": "project",
180
+ "ok": True,
181
+ "detail": f"Found {config_path}" if config_path.is_file() else "No project config (optional)",
182
+ })
183
+ passed = all(check["ok"] for check in checks)
184
+ if args.format == "json":
185
+ print(json.dumps({"status": "PASS" if passed else "FAIL", "checks": checks}, indent=2))
186
+ else:
187
+ print("OpsScript Gate doctor")
188
+ for check in checks:
189
+ marker = "PASS" if check["ok"] else "FAIL"
190
+ print(f"[{marker}] {check['name']}: {check['detail']}")
191
+ if not passed:
192
+ print("Run `opsscript-gate init` after fixing the failed check, then preview with `opsscript-gate run --dry-run`.")
193
+ return 0 if passed else 1
194
+
152
195
  if args.command == "run":
153
196
  try:
154
197
  config = load_config(args.config)
@@ -63,7 +63,7 @@ jobs:
63
63
  - uses: actions/checkout@v7
64
64
  with:
65
65
  persist-credentials: false
66
- - uses: Mresyzz/opsscript-gate@v0.6.2
66
+ - uses: Mresyzz/opsscript-gate@v0.6.3
67
67
  with:
68
68
  config: .opsscript-gate.json
69
69
  """,
@@ -8,6 +8,7 @@ from opsscript_gate.cli import main
8
8
  from opsscript_gate.config import CONFIG_NAME, init_project, load_config
9
9
  from opsscript_gate.discovery import discover_changed_scripts, discover_scripts, is_shell_script
10
10
  from opsscript_gate.models import RunReport, SingleResult, DistroStatus
11
+ from opsscript_gate.runner import DockerDaemonError
11
12
 
12
13
 
13
14
  @pytest.fixture
@@ -35,6 +36,27 @@ def test_init_preview_and_no_overwrite(project, capsys):
35
36
  assert Path(CONFIG_NAME).read_bytes() == original
36
37
 
37
38
 
39
+ def test_doctor_reports_local_prerequisites(project, capsys):
40
+ with patch("opsscript_gate.cli.get_docker_client"):
41
+ assert main(["doctor"]) == 0
42
+ output = capsys.readouterr().out
43
+ assert "OpsScript Gate doctor" in output
44
+ assert "[PASS] python" in output
45
+ assert "[PASS] docker" in output
46
+ assert "No project config (optional)" in output
47
+
48
+
49
+ def test_doctor_json_reports_docker_failure(project, capsys):
50
+ with patch(
51
+ "opsscript_gate.cli.get_docker_client",
52
+ side_effect=DockerDaemonError("Docker is not running"),
53
+ ):
54
+ assert main(["doctor", "--format", "json"]) == 1
55
+ report = json.loads(capsys.readouterr().out)
56
+ assert report["status"] == "FAIL"
57
+ assert next(check for check in report["checks"] if check["name"] == "docker")["ok"] is False
58
+
59
+
38
60
  def test_init_existing_workflow_leaves_config_absent(project):
39
61
  workflow = project / ".github/workflows/opsscript-gate.yml"
40
62
  workflow.parent.mkdir(parents=True)
File without changes