docker2wslc 0.1.0__tar.gz → 0.2.0__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.
- {docker2wslc-0.1.0 → docker2wslc-0.2.0}/PKG-INFO +11 -8
- {docker2wslc-0.1.0 → docker2wslc-0.2.0}/README.md +10 -7
- {docker2wslc-0.1.0 → docker2wslc-0.2.0}/pyproject.toml +1 -1
- {docker2wslc-0.1.0 → docker2wslc-0.2.0}/src/docker2wslc/__init__.py +1 -1
- {docker2wslc-0.1.0 → docker2wslc-0.2.0}/src/docker2wslc/compose.py +90 -7
- docker2wslc-0.2.0/src/docker2wslc/rules.json +572 -0
- {docker2wslc-0.1.0 → docker2wslc-0.2.0}/src/docker2wslc/translate.py +64 -5
- docker2wslc-0.1.0/src/docker2wslc/rules.json +0 -167
- {docker2wslc-0.1.0 → docker2wslc-0.2.0}/.gitignore +0 -0
- {docker2wslc-0.1.0 → docker2wslc-0.2.0}/LICENSE +0 -0
- {docker2wslc-0.1.0 → docker2wslc-0.2.0}/src/docker2wslc/cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docker2wslc
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Translate Docker commands and Compose files to wslc, the native WSL container runtime
|
|
5
5
|
Project-URL: Homepage, https://wslcontainers.com
|
|
6
6
|
Project-URL: Documentation, https://wslcontainers.com/reference/cheatsheet/
|
|
@@ -46,13 +46,13 @@ pip install 'docker2wslc[yaml]' # + Compose analysis
|
|
|
46
46
|
|
|
47
47
|
```console
|
|
48
48
|
$ docker2wslc convert docker run --gpus all --restart always -p 8080:80 nginx
|
|
49
|
-
wslc run --
|
|
49
|
+
wslc run --gpus all -p 8080:80 nginx
|
|
50
50
|
|
|
51
51
|
Migration notes
|
|
52
52
|
WARN Restart policies are not implemented in the wslc preview. Flag dropped — use a
|
|
53
53
|
Windows scheduled task or a wrapper script for auto-restart.
|
|
54
|
-
INFO
|
|
55
|
-
`--
|
|
54
|
+
INFO `--gpus` is native in wslc 2.9.4, but the host must actually have the GPU: on a
|
|
55
|
+
machine without one, `--gpus all` fails at container init with an ldconfig error.
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
Commands come from arguments, a file, or stdin:
|
|
@@ -74,15 +74,18 @@ $ docker2wslc compose docker-compose.yml
|
|
|
74
74
|
|
|
75
75
|
wslc volume create pgdata
|
|
76
76
|
wslc network create backend
|
|
77
|
+
# start order matters: web -> db
|
|
77
78
|
|
|
78
79
|
# service: web
|
|
79
80
|
wslc run -d --name web -e NGINX_HOST=localhost -p 8080:80 --network backend nginx:alpine
|
|
81
|
+
! networks: Bridge-only. Create with wslc network create before running.
|
|
80
82
|
x depends_on: No dependency ordering. Start services in order yourself and add readiness waits.
|
|
81
83
|
x restart: No restart policies in the wslc preview.
|
|
82
84
|
|
|
83
85
|
# service: db
|
|
84
|
-
wslc run -d --name db -v pgdata:/var/lib/postgresql/data postgres:16-alpine
|
|
85
|
-
|
|
86
|
+
wslc run -d --name db -v pgdata:/var/lib/postgresql/data --network backend --health-cmd 'pg_isready -U postgres' --health-interval 10s --health-retries 5 postgres:16-alpine
|
|
87
|
+
! volumes: Named volumes must be created first with wslc volume create. Windows paths go over VirtioFS.
|
|
88
|
+
! networks: Bridge-only. Create with wslc network create before running.
|
|
86
89
|
```
|
|
87
90
|
|
|
88
91
|
## Lint a repository
|
|
@@ -132,10 +135,10 @@ Worth knowing before you migrate. These are runtime limitations, not gaps in thi
|
|
|
132
135
|
- **No Compose runtime** — translate services by hand, see the [migration guide](https://wslcontainers.com/guides/compose/)
|
|
133
136
|
- **No restart policies** — use a scheduled task
|
|
134
137
|
- **No `--platform`** — host architecture only
|
|
135
|
-
- **No
|
|
138
|
+
- **No `depends_on` gating** — health flags work (`--health-cmd` et al), but nothing waits on health state for you
|
|
136
139
|
- **No Docker socket or Engine API** — [Testcontainers, Portainer and act cannot attach](https://wslcontainers.com/guides/testcontainers/)
|
|
137
140
|
- **No buildx / bake** — single-platform `wslc build` only
|
|
138
|
-
- **GPU
|
|
141
|
+
- **GPU** — `--gpus all`, the same flag as Docker. It is `--device` that wslc lacks.
|
|
139
142
|
|
|
140
143
|
CLI-driven tooling ports to wslc. API-driven tooling does not. That single distinction
|
|
141
144
|
explains most migration surprises — including why
|
|
@@ -15,13 +15,13 @@ pip install 'docker2wslc[yaml]' # + Compose analysis
|
|
|
15
15
|
|
|
16
16
|
```console
|
|
17
17
|
$ docker2wslc convert docker run --gpus all --restart always -p 8080:80 nginx
|
|
18
|
-
wslc run --
|
|
18
|
+
wslc run --gpus all -p 8080:80 nginx
|
|
19
19
|
|
|
20
20
|
Migration notes
|
|
21
21
|
WARN Restart policies are not implemented in the wslc preview. Flag dropped — use a
|
|
22
22
|
Windows scheduled task or a wrapper script for auto-restart.
|
|
23
|
-
INFO
|
|
24
|
-
`--
|
|
23
|
+
INFO `--gpus` is native in wslc 2.9.4, but the host must actually have the GPU: on a
|
|
24
|
+
machine without one, `--gpus all` fails at container init with an ldconfig error.
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
Commands come from arguments, a file, or stdin:
|
|
@@ -43,15 +43,18 @@ $ docker2wslc compose docker-compose.yml
|
|
|
43
43
|
|
|
44
44
|
wslc volume create pgdata
|
|
45
45
|
wslc network create backend
|
|
46
|
+
# start order matters: web -> db
|
|
46
47
|
|
|
47
48
|
# service: web
|
|
48
49
|
wslc run -d --name web -e NGINX_HOST=localhost -p 8080:80 --network backend nginx:alpine
|
|
50
|
+
! networks: Bridge-only. Create with wslc network create before running.
|
|
49
51
|
x depends_on: No dependency ordering. Start services in order yourself and add readiness waits.
|
|
50
52
|
x restart: No restart policies in the wslc preview.
|
|
51
53
|
|
|
52
54
|
# service: db
|
|
53
|
-
wslc run -d --name db -v pgdata:/var/lib/postgresql/data postgres:16-alpine
|
|
54
|
-
|
|
55
|
+
wslc run -d --name db -v pgdata:/var/lib/postgresql/data --network backend --health-cmd 'pg_isready -U postgres' --health-interval 10s --health-retries 5 postgres:16-alpine
|
|
56
|
+
! volumes: Named volumes must be created first with wslc volume create. Windows paths go over VirtioFS.
|
|
57
|
+
! networks: Bridge-only. Create with wslc network create before running.
|
|
55
58
|
```
|
|
56
59
|
|
|
57
60
|
## Lint a repository
|
|
@@ -101,10 +104,10 @@ Worth knowing before you migrate. These are runtime limitations, not gaps in thi
|
|
|
101
104
|
- **No Compose runtime** — translate services by hand, see the [migration guide](https://wslcontainers.com/guides/compose/)
|
|
102
105
|
- **No restart policies** — use a scheduled task
|
|
103
106
|
- **No `--platform`** — host architecture only
|
|
104
|
-
- **No
|
|
107
|
+
- **No `depends_on` gating** — health flags work (`--health-cmd` et al), but nothing waits on health state for you
|
|
105
108
|
- **No Docker socket or Engine API** — [Testcontainers, Portainer and act cannot attach](https://wslcontainers.com/guides/testcontainers/)
|
|
106
109
|
- **No buildx / bake** — single-platform `wslc build` only
|
|
107
|
-
- **GPU
|
|
110
|
+
- **GPU** — `--gpus all`, the same flag as Docker. It is `--device` that wslc lacks.
|
|
108
111
|
|
|
109
112
|
CLI-driven tooling ports to wslc. API-driven tooling does not. That single distinction
|
|
110
113
|
explains most migration surprises — including why
|
|
@@ -4,7 +4,7 @@ wslc is the native Linux container runtime in the Windows Subsystem for Linux.
|
|
|
4
4
|
Docs and an interactive converter: https://wslcontainers.com
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__version__ = "0.
|
|
7
|
+
__version__ = "0.2.0"
|
|
8
8
|
|
|
9
9
|
from .compose import ComposeReport, analyse
|
|
10
10
|
from .translate import Note, Result, load_rules, translate, translate_line
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import re
|
|
5
6
|
from dataclasses import dataclass, field
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
@@ -65,11 +66,62 @@ def _as_list(value: Any) -> list[str]:
|
|
|
65
66
|
return [str(value)]
|
|
66
67
|
|
|
67
68
|
|
|
69
|
+
def _upper_size(value: Any) -> str:
|
|
70
|
+
"""wslc rejects lowercase size units: 512m -> Invalid memory argument value."""
|
|
71
|
+
return re.sub(
|
|
72
|
+
r"^(\d+(?:\.\d+)?)\s*([kmgtKMGT])(b?|B?)$",
|
|
73
|
+
lambda m: m.group(1) + m.group(2).upper() + m.group(3).upper(),
|
|
74
|
+
str(value),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
_UNIT_SECONDS = {"h": 3600, "m": 60, "s": 1, "ms": 0.001, "us": 0.000001}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _stop_seconds(value: Any) -> str:
|
|
82
|
+
"""--stop-timeout takes plain seconds; stop_grace_period is a Go duration.
|
|
83
|
+
|
|
84
|
+
Compose allows compound forms like `1m30s`, so a single-unit regex is not
|
|
85
|
+
enough -- `1m30s` must become 90, not pass through unchanged.
|
|
86
|
+
"""
|
|
87
|
+
raw = str(value).strip()
|
|
88
|
+
if re.fullmatch(r"\d+", raw):
|
|
89
|
+
return raw
|
|
90
|
+
parts = re.findall(r"(\d+(?:\.\d+)?)(ms|us|h|m|s)", raw)
|
|
91
|
+
if not parts or "".join(n + u for n, u in parts) != raw:
|
|
92
|
+
return raw
|
|
93
|
+
total = sum(float(n) * _UNIT_SECONDS[u] for n, u in parts)
|
|
94
|
+
return str(round(total))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _shell_quote(value: Any) -> str:
|
|
98
|
+
"""Args are joined into one shell line, so whitespace must be quoted."""
|
|
99
|
+
s = str(value)
|
|
100
|
+
if s == "":
|
|
101
|
+
return "''"
|
|
102
|
+
if not re.search(r"""[\s"'$`\\|&;<>()*?!#~\[\]{}]""", s):
|
|
103
|
+
return s
|
|
104
|
+
return "'" + s.replace("'", "'\\''") + "'"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _health_cmd(test: Any) -> str:
|
|
108
|
+
"""Compose test: is a string, or a list led by CMD / CMD-SHELL / NONE."""
|
|
109
|
+
if not isinstance(test, list):
|
|
110
|
+
return str(test)
|
|
111
|
+
parts = [str(p) for p in test]
|
|
112
|
+
head = (parts[0] if parts else "").upper()
|
|
113
|
+
if head in ("CMD-SHELL", "CMD"):
|
|
114
|
+
return " ".join(parts[1:])
|
|
115
|
+
if head == "NONE":
|
|
116
|
+
return ""
|
|
117
|
+
return " ".join(parts)
|
|
118
|
+
|
|
119
|
+
|
|
68
120
|
def _build_run(name: str, svc: dict[str, Any]) -> str:
|
|
69
121
|
args: list[str] = ["wslc", "run", "-d", "--name", svc.get("container_name") or name]
|
|
70
122
|
|
|
71
123
|
for env in _as_list(svc.get("environment")):
|
|
72
|
-
args += ["-e", env]
|
|
124
|
+
args += ["-e", _shell_quote(env)]
|
|
73
125
|
for env_file in _as_list(svc.get("env_file")):
|
|
74
126
|
args += ["--env-file", env_file]
|
|
75
127
|
for port in _as_list(svc.get("ports")):
|
|
@@ -78,14 +130,17 @@ def _build_run(name: str, svc: dict[str, Any]) -> str:
|
|
|
78
130
|
args += ["-v", vol]
|
|
79
131
|
for net in _as_list(svc.get("networks")):
|
|
80
132
|
args += ["--network", net]
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
args += ["--device", dev]
|
|
133
|
+
# cap_add / cap_drop / devices / privileged / security_opt / expose are
|
|
134
|
+
# deliberately NOT emitted: wslc 2.9.4 has no such flags, so emitting them
|
|
135
|
+
# yields "Argument name was not recognized". They surface as findings.
|
|
85
136
|
for label in _as_list(svc.get("labels")):
|
|
86
|
-
args += ["--label", label]
|
|
137
|
+
args += ["--label", _shell_quote(label)]
|
|
87
138
|
for tmp in _as_list(svc.get("tmpfs")):
|
|
88
139
|
args += ["--tmpfs", tmp]
|
|
140
|
+
for dns in _as_list(svc.get("dns")):
|
|
141
|
+
args += ["--dns", str(dns)]
|
|
142
|
+
for ulimit in _as_list(svc.get("ulimits")):
|
|
143
|
+
args += ["--ulimit", _shell_quote(ulimit)]
|
|
89
144
|
|
|
90
145
|
if svc.get("working_dir"):
|
|
91
146
|
args += ["-w", str(svc["working_dir"])]
|
|
@@ -93,9 +148,37 @@ def _build_run(name: str, svc: dict[str, Any]) -> str:
|
|
|
93
148
|
args += ["-u", str(svc["user"])]
|
|
94
149
|
if svc.get("hostname"):
|
|
95
150
|
args += ["--hostname", str(svc["hostname"])]
|
|
151
|
+
if svc.get("domainname"):
|
|
152
|
+
args += ["--domainname", str(svc["domainname"])]
|
|
153
|
+
if svc.get("shm_size"):
|
|
154
|
+
args += ["--shm-size", _upper_size(svc["shm_size"])]
|
|
155
|
+
if svc.get("mem_limit"):
|
|
156
|
+
args += ["-m", _upper_size(svc["mem_limit"])]
|
|
157
|
+
if svc.get("cpus"):
|
|
158
|
+
args += ["--cpus", str(svc["cpus"])]
|
|
159
|
+
if svc.get("stop_signal"):
|
|
160
|
+
args += ["--stop-signal", str(svc["stop_signal"])]
|
|
161
|
+
if svc.get("stop_grace_period"):
|
|
162
|
+
args += ["--stop-timeout", _stop_seconds(svc["stop_grace_period"])]
|
|
163
|
+
# healthcheck maps flag-for-flag onto wslc run --health-* (verified 2.9.4.0).
|
|
164
|
+
hc = svc.get("healthcheck")
|
|
165
|
+
if isinstance(hc, dict):
|
|
166
|
+
if hc.get("disable") in (True, "true"):
|
|
167
|
+
args.append("--no-healthcheck")
|
|
168
|
+
else:
|
|
169
|
+
if hc.get("test"):
|
|
170
|
+
args += ["--health-cmd", _shell_quote(_health_cmd(hc["test"]))]
|
|
171
|
+
if hc.get("interval"):
|
|
172
|
+
args += ["--health-interval", str(hc["interval"])]
|
|
173
|
+
if hc.get("retries"):
|
|
174
|
+
args += ["--health-retries", str(hc["retries"])]
|
|
175
|
+
if hc.get("timeout"):
|
|
176
|
+
args += ["--health-timeout", str(hc["timeout"])]
|
|
177
|
+
if hc.get("start_period"):
|
|
178
|
+
args += ["--health-start-period", str(hc["start_period"])]
|
|
96
179
|
if svc.get("entrypoint"):
|
|
97
180
|
ep = svc["entrypoint"]
|
|
98
|
-
args += ["--entrypoint", ep if isinstance(ep, str) else " ".join(ep)]
|
|
181
|
+
args += ["--entrypoint", _shell_quote(ep if isinstance(ep, str) else " ".join(ep))]
|
|
99
182
|
if svc.get("stdin_open"):
|
|
100
183
|
args.append("-i")
|
|
101
184
|
if svc.get("tty"):
|
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Single source of truth for Docker -> wslc translation. Consumed by the Python package, the npm CLI, the MCP server and the VS Code extension. Do not fork this logic per language. Every claim here is verified against a real wslc on a windows-2025 runner; see `verification`.",
|
|
3
|
+
"schemaVersion": 2,
|
|
4
|
+
"wslcPreview": "2.9.4.0",
|
|
5
|
+
"identical": [
|
|
6
|
+
"run",
|
|
7
|
+
"build",
|
|
8
|
+
"pull",
|
|
9
|
+
"push",
|
|
10
|
+
"stop",
|
|
11
|
+
"start",
|
|
12
|
+
"exec",
|
|
13
|
+
"logs",
|
|
14
|
+
"inspect",
|
|
15
|
+
"stats",
|
|
16
|
+
"tag",
|
|
17
|
+
"login",
|
|
18
|
+
"logout",
|
|
19
|
+
"kill",
|
|
20
|
+
"save",
|
|
21
|
+
"load",
|
|
22
|
+
"attach",
|
|
23
|
+
"version"
|
|
24
|
+
],
|
|
25
|
+
"renamed": {
|
|
26
|
+
"ps": "container list",
|
|
27
|
+
"images": "image list",
|
|
28
|
+
"rmi": "image remove",
|
|
29
|
+
"rm": "container remove",
|
|
30
|
+
"create": "container create",
|
|
31
|
+
"cp": "container cp"
|
|
32
|
+
},
|
|
33
|
+
"renamedFlags": {
|
|
34
|
+
"ps": {
|
|
35
|
+
"-a": "--all",
|
|
36
|
+
"--all": "--all",
|
|
37
|
+
"-q": "--quiet",
|
|
38
|
+
"--quiet": "--quiet"
|
|
39
|
+
},
|
|
40
|
+
"images": {
|
|
41
|
+
"-a": "--all",
|
|
42
|
+
"--all": "--all"
|
|
43
|
+
},
|
|
44
|
+
"rm": {
|
|
45
|
+
"-f": "--force",
|
|
46
|
+
"--force": "--force"
|
|
47
|
+
},
|
|
48
|
+
"rmi": {
|
|
49
|
+
"-f": "--force",
|
|
50
|
+
"--force": "--force"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"unsupported": {
|
|
54
|
+
"swarm": "Swarm orchestration has no wslc equivalent. Use Kubernetes or keep Docker for this workload.",
|
|
55
|
+
"service": "Swarm services are not supported by wslc.",
|
|
56
|
+
"stack": "Swarm stacks are not supported by wslc.",
|
|
57
|
+
"node": "Swarm node management is not supported by wslc.",
|
|
58
|
+
"secret": "Swarm secrets are not supported. Pass values with -e or mount a file.",
|
|
59
|
+
"config": "Swarm configs are not supported by wslc.",
|
|
60
|
+
"plugin": "Docker plugins are not supported by wslc.",
|
|
61
|
+
"context": "wslc has a single local runtime, so there are no contexts to switch.",
|
|
62
|
+
"buildx": "buildx / bake is not available. Use wslc build for single-platform images.",
|
|
63
|
+
"manifest": "Manifest lists cannot be created with wslc in the preview.",
|
|
64
|
+
"trust": "Docker Content Trust is Docker-specific and has no wslc equivalent.",
|
|
65
|
+
"checkpoint": "Checkpoint/restore is not implemented in wslc.",
|
|
66
|
+
"restart": "No restart command in wslc 2.9.4. Run `wslc stop <id>` then `wslc start <id>`.",
|
|
67
|
+
"pause": "Pause/unpause (SIGSTOP freezing) is not implemented in wslc 2.9.4.",
|
|
68
|
+
"unpause": "Pause/unpause is not implemented in wslc 2.9.4.",
|
|
69
|
+
"info": "No `system info`. `wslc system` only exposes `session`. Use `wslc version` for build info.",
|
|
70
|
+
"top": "No `top`. Use `wslc exec <id> ps aux` inside the container.",
|
|
71
|
+
"wait": "No `wait`. Poll `wslc container list` or run the container in the foreground without --detach.",
|
|
72
|
+
"port": "No `port` command. Published ports are shown in the PORTS column of `wslc container list`.",
|
|
73
|
+
"rename": "Containers cannot be renamed in wslc 2.9.4. Recreate with the desired --name.",
|
|
74
|
+
"diff": "No filesystem diff. `wslc export <id>` writes the container filesystem to a tar for inspection.",
|
|
75
|
+
"commit": "No commit. Use `wslc export` then `wslc import`, or build from a Dockerfile.",
|
|
76
|
+
"history": "wslc 2.9.4 has no image history. `wslc image inspect` shows config but not per-layer history.",
|
|
77
|
+
"search": "wslc 2.9.4 cannot search a registry. Browse Docker Hub / the registry web UI instead."
|
|
78
|
+
},
|
|
79
|
+
"flags": {
|
|
80
|
+
"--platform": {
|
|
81
|
+
"action": "drop",
|
|
82
|
+
"takesValue": true,
|
|
83
|
+
"severity": "warn",
|
|
84
|
+
"note": "`--platform` is dropped: wslc builds and runs for the host architecture only in the preview."
|
|
85
|
+
},
|
|
86
|
+
"--restart": {
|
|
87
|
+
"action": "drop",
|
|
88
|
+
"takesValue": true,
|
|
89
|
+
"severity": "warn",
|
|
90
|
+
"note": "Restart policies are not implemented in the wslc preview. Flag dropped — use a Windows scheduled task or a wrapper script for auto-restart."
|
|
91
|
+
},
|
|
92
|
+
"--gpus": {
|
|
93
|
+
"action": "keep",
|
|
94
|
+
"takesValue": true,
|
|
95
|
+
"severity": "info",
|
|
96
|
+
"note": "`--gpus` is native in wslc 2.9.4 (`--gpus all`). Do NOT rewrite it to `--device`: wslc has no --device flag. On a host without an NVIDIA GPU the run fails at container init with an ldconfig error.",
|
|
97
|
+
"noteAlways": "`--gpus` is native in wslc 2.9.4, but the host must actually have the GPU: on a machine without one, `--gpus all` fails at container init with an ldconfig error."
|
|
98
|
+
},
|
|
99
|
+
"--network": {
|
|
100
|
+
"action": "conditional",
|
|
101
|
+
"takesValue": true,
|
|
102
|
+
"whenValue": {
|
|
103
|
+
"host": {
|
|
104
|
+
"action": "error",
|
|
105
|
+
"severity": "error",
|
|
106
|
+
"note": "wslc rejects host networking outright: `host mode networking is not supported` (exit 1). It is not implicit and cannot be dropped silently. `--network none` works."
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"severity": "info",
|
|
110
|
+
"note": "Custom networks are supported: create with `wslc network create`, then `wslc network connect`. `none` is valid; `host` is rejected."
|
|
111
|
+
},
|
|
112
|
+
"--volume": {
|
|
113
|
+
"action": "keep",
|
|
114
|
+
"takesValue": true,
|
|
115
|
+
"aliases": [
|
|
116
|
+
"-v",
|
|
117
|
+
"--mount"
|
|
118
|
+
],
|
|
119
|
+
"severity": "info",
|
|
120
|
+
"noteWhenWindowsPath": "Windows host paths are shared over VirtioFS. Use forward slashes (`C:/work:/app`) and expect Linux-style permissions on the container side."
|
|
121
|
+
},
|
|
122
|
+
"--memory": {
|
|
123
|
+
"action": "keep",
|
|
124
|
+
"takesValue": true,
|
|
125
|
+
"severity": "info",
|
|
126
|
+
"note": "Supported (`-m`). Unit suffix must be UPPERCASE: `512M` works, `512m` is rejected with `Invalid memory argument value`. Plain byte counts also work. Expect a kernel warning that swap limits are unavailable.",
|
|
127
|
+
"requireValuePattern": "^[0-9]+([KMGT]|[KMGT]B)?$",
|
|
128
|
+
"noteWhenValueRejected": "wslc rejects lowercase size units: `--memory 512m` fails with `Invalid memory argument value`. Use an UPPERCASE suffix (512M, 1G) or a plain byte count.",
|
|
129
|
+
"aliases": [
|
|
130
|
+
"-m"
|
|
131
|
+
],
|
|
132
|
+
"fixValueCase": "upperSizeSuffix",
|
|
133
|
+
"noteWhenValueFixed": "Rewrote the size to use an UPPERCASE unit suffix: wslc rejects lowercase units (`Invalid memory argument value`) while Docker accepts both."
|
|
134
|
+
},
|
|
135
|
+
"--shm-size": {
|
|
136
|
+
"action": "keep",
|
|
137
|
+
"takesValue": true,
|
|
138
|
+
"severity": "info",
|
|
139
|
+
"note": "Supported. Same uppercase-unit rule as --memory: `64M` works, `64m` is rejected.",
|
|
140
|
+
"requireValuePattern": "^[0-9]+([KMGT]|[KMGT]B)?$",
|
|
141
|
+
"noteWhenValueRejected": "wslc rejects lowercase size units: `--shm-size 64m` fails with `Invalid shm-size argument value`. Use an UPPERCASE suffix (64M, 1G).",
|
|
142
|
+
"fixValueCase": "upperSizeSuffix",
|
|
143
|
+
"noteWhenValueFixed": "Rewrote the size to use an UPPERCASE unit suffix: wslc rejects lowercase units (`Invalid shm-size argument value`) while Docker accepts both."
|
|
144
|
+
},
|
|
145
|
+
"--cpus": {
|
|
146
|
+
"action": "keep",
|
|
147
|
+
"takesValue": true,
|
|
148
|
+
"severity": "info",
|
|
149
|
+
"note": "Supported, accepts fractional values (0.5, 1, 2.5)."
|
|
150
|
+
},
|
|
151
|
+
"--ulimit": {
|
|
152
|
+
"action": "keep",
|
|
153
|
+
"takesValue": true,
|
|
154
|
+
"severity": "info",
|
|
155
|
+
"note": "Supported, e.g. `--ulimit nofile=1024:1024`."
|
|
156
|
+
},
|
|
157
|
+
"--stop-timeout": {
|
|
158
|
+
"action": "keep",
|
|
159
|
+
"takesValue": true,
|
|
160
|
+
"severity": "info",
|
|
161
|
+
"note": "Supported, seconds; -1 means no timeout."
|
|
162
|
+
},
|
|
163
|
+
"--health-cmd": {
|
|
164
|
+
"action": "keep",
|
|
165
|
+
"takesValue": true,
|
|
166
|
+
"severity": "info",
|
|
167
|
+
"note": "Supported. wslc has the full health-check family: --health-cmd, --health-interval, --health-retries, --health-start-period, --health-timeout, --no-healthcheck."
|
|
168
|
+
},
|
|
169
|
+
"--device": {
|
|
170
|
+
"action": "drop",
|
|
171
|
+
"takesValue": true,
|
|
172
|
+
"severity": "warn",
|
|
173
|
+
"note": "wslc 2.9.4 has no --device flag (`Argument name was not recognized`). For GPUs use --gpus."
|
|
174
|
+
},
|
|
175
|
+
"--publish": {
|
|
176
|
+
"action": "keep",
|
|
177
|
+
"takesValue": true,
|
|
178
|
+
"aliases": [
|
|
179
|
+
"-p"
|
|
180
|
+
],
|
|
181
|
+
"severity": "info",
|
|
182
|
+
"note": "Supported (`-p`). `-P/--publish-all` publishes all exposed ports."
|
|
183
|
+
},
|
|
184
|
+
"--cap-add": {
|
|
185
|
+
"action": "drop",
|
|
186
|
+
"takesValue": true,
|
|
187
|
+
"severity": "warn",
|
|
188
|
+
"note": "wslc 2.9.4 has no --cap-add (`Argument name was not recognized`). There is no way to grant extra capabilities; --privileged and --security-opt are missing too."
|
|
189
|
+
},
|
|
190
|
+
"--cap-drop": {
|
|
191
|
+
"action": "drop",
|
|
192
|
+
"takesValue": true,
|
|
193
|
+
"severity": "warn",
|
|
194
|
+
"note": "wslc 2.9.4 has no --cap-drop (`Argument name was not recognized`)."
|
|
195
|
+
},
|
|
196
|
+
"--privileged": {
|
|
197
|
+
"action": "drop",
|
|
198
|
+
"takesValue": false,
|
|
199
|
+
"severity": "warn",
|
|
200
|
+
"note": "wslc 2.9.4 has no --privileged (`Argument name was not recognized`)."
|
|
201
|
+
},
|
|
202
|
+
"--security-opt": {
|
|
203
|
+
"action": "drop",
|
|
204
|
+
"takesValue": true,
|
|
205
|
+
"severity": "warn",
|
|
206
|
+
"note": "wslc 2.9.4 has no --security-opt (`Argument name was not recognized`)."
|
|
207
|
+
},
|
|
208
|
+
"--expose": {
|
|
209
|
+
"action": "drop",
|
|
210
|
+
"takesValue": true,
|
|
211
|
+
"severity": "warn",
|
|
212
|
+
"note": "wslc 2.9.4 has no --expose. Publish the port with -p, or use -P/--publish-all to publish every port the image exposes."
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
"compose": {
|
|
216
|
+
"supported": false,
|
|
217
|
+
"note": "wslc has no built-in Compose runtime. Translate each service into a wslc run call, or drive them from a PowerShell/bash script.",
|
|
218
|
+
"docsUrl": "https://wslcontainers.com/guides/compose/",
|
|
219
|
+
"keys": {
|
|
220
|
+
"image": {
|
|
221
|
+
"status": "ok",
|
|
222
|
+
"maps": "positional image argument"
|
|
223
|
+
},
|
|
224
|
+
"build": {
|
|
225
|
+
"status": "ok",
|
|
226
|
+
"maps": "wslc build -t <name>"
|
|
227
|
+
},
|
|
228
|
+
"command": {
|
|
229
|
+
"status": "ok",
|
|
230
|
+
"maps": "trailing command args"
|
|
231
|
+
},
|
|
232
|
+
"entrypoint": {
|
|
233
|
+
"status": "ok",
|
|
234
|
+
"maps": "--entrypoint"
|
|
235
|
+
},
|
|
236
|
+
"environment": {
|
|
237
|
+
"status": "ok",
|
|
238
|
+
"maps": "-e KEY=VALUE"
|
|
239
|
+
},
|
|
240
|
+
"env_file": {
|
|
241
|
+
"status": "ok",
|
|
242
|
+
"maps": "--env-file"
|
|
243
|
+
},
|
|
244
|
+
"ports": {
|
|
245
|
+
"status": "ok",
|
|
246
|
+
"maps": "-p HOST:CONTAINER"
|
|
247
|
+
},
|
|
248
|
+
"volumes": {
|
|
249
|
+
"status": "partial",
|
|
250
|
+
"maps": "-v",
|
|
251
|
+
"note": "Named volumes must be created first with wslc volume create. Windows paths go over VirtioFS."
|
|
252
|
+
},
|
|
253
|
+
"networks": {
|
|
254
|
+
"status": "partial",
|
|
255
|
+
"maps": "--network",
|
|
256
|
+
"note": "Bridge-only. Create with wslc network create before running."
|
|
257
|
+
},
|
|
258
|
+
"working_dir": {
|
|
259
|
+
"status": "ok",
|
|
260
|
+
"maps": "-w"
|
|
261
|
+
},
|
|
262
|
+
"user": {
|
|
263
|
+
"status": "ok",
|
|
264
|
+
"maps": "-u"
|
|
265
|
+
},
|
|
266
|
+
"hostname": {
|
|
267
|
+
"status": "ok",
|
|
268
|
+
"maps": "--hostname"
|
|
269
|
+
},
|
|
270
|
+
"container_name": {
|
|
271
|
+
"status": "ok",
|
|
272
|
+
"maps": "--name"
|
|
273
|
+
},
|
|
274
|
+
"labels": {
|
|
275
|
+
"status": "ok",
|
|
276
|
+
"maps": "--label"
|
|
277
|
+
},
|
|
278
|
+
"cap_add": {
|
|
279
|
+
"status": "missing",
|
|
280
|
+
"note": "wslc 2.9.4 has no --cap-add (`Argument name was not recognized`), and no --privileged or --security-opt either."
|
|
281
|
+
},
|
|
282
|
+
"devices": {
|
|
283
|
+
"status": "missing",
|
|
284
|
+
"note": "wslc 2.9.4 has no --device. GPUs go through --gpus; there is no generic device passthrough."
|
|
285
|
+
},
|
|
286
|
+
"tmpfs": {
|
|
287
|
+
"status": "ok",
|
|
288
|
+
"maps": "--tmpfs"
|
|
289
|
+
},
|
|
290
|
+
"stdin_open": {
|
|
291
|
+
"status": "ok",
|
|
292
|
+
"maps": "-i"
|
|
293
|
+
},
|
|
294
|
+
"tty": {
|
|
295
|
+
"status": "ok",
|
|
296
|
+
"maps": "-t"
|
|
297
|
+
},
|
|
298
|
+
"restart": {
|
|
299
|
+
"status": "missing",
|
|
300
|
+
"note": "No restart policies in the wslc preview."
|
|
301
|
+
},
|
|
302
|
+
"healthcheck": {
|
|
303
|
+
"status": "ok",
|
|
304
|
+
"maps": "--health-cmd / --health-interval / --health-retries / --health-timeout / --health-start-period",
|
|
305
|
+
"emit": "health",
|
|
306
|
+
"note": "wslc run implements all five health flags plus --no-healthcheck. Nothing reads the compose file, so the flags are emitted onto the run command for you. What is still missing is depends_on gating on health state."
|
|
307
|
+
},
|
|
308
|
+
"depends_on": {
|
|
309
|
+
"status": "missing",
|
|
310
|
+
"note": "No dependency ordering. Start services in order yourself and add readiness waits."
|
|
311
|
+
},
|
|
312
|
+
"deploy": {
|
|
313
|
+
"status": "missing",
|
|
314
|
+
"note": "Swarm-only section, ignored entirely."
|
|
315
|
+
},
|
|
316
|
+
"profiles": {
|
|
317
|
+
"status": "missing",
|
|
318
|
+
"note": "Compose-only concept."
|
|
319
|
+
},
|
|
320
|
+
"extends": {
|
|
321
|
+
"status": "missing",
|
|
322
|
+
"note": "Compose-only concept."
|
|
323
|
+
},
|
|
324
|
+
"configs": {
|
|
325
|
+
"status": "missing",
|
|
326
|
+
"note": "Swarm configs are unsupported; mount a file instead."
|
|
327
|
+
},
|
|
328
|
+
"secrets": {
|
|
329
|
+
"status": "missing",
|
|
330
|
+
"note": "Swarm secrets are unsupported; use -e or a mounted file."
|
|
331
|
+
},
|
|
332
|
+
"cap_drop": {
|
|
333
|
+
"status": "missing",
|
|
334
|
+
"note": "wslc 2.9.4 has no --cap-drop."
|
|
335
|
+
},
|
|
336
|
+
"expose": {
|
|
337
|
+
"status": "missing",
|
|
338
|
+
"note": "No --expose on wslc run. Publish with ports: (-p), or use -P to publish everything the image exposes."
|
|
339
|
+
},
|
|
340
|
+
"network_mode": {
|
|
341
|
+
"status": "missing",
|
|
342
|
+
"note": "network_mode: host is refused with `host mode networking is not supported`. Use a user-defined network plus published ports."
|
|
343
|
+
},
|
|
344
|
+
"privileged": {
|
|
345
|
+
"status": "missing",
|
|
346
|
+
"note": "No --privileged on wslc run."
|
|
347
|
+
},
|
|
348
|
+
"security_opt": {
|
|
349
|
+
"status": "missing",
|
|
350
|
+
"note": "No --security-opt on wslc run."
|
|
351
|
+
},
|
|
352
|
+
"shm_size": {
|
|
353
|
+
"status": "ok",
|
|
354
|
+
"maps": "--shm-size",
|
|
355
|
+
"note": "Unit suffix must be UPPERCASE (64M, not 64m)."
|
|
356
|
+
},
|
|
357
|
+
"mem_limit": {
|
|
358
|
+
"status": "ok",
|
|
359
|
+
"maps": "-m",
|
|
360
|
+
"note": "Unit suffix must be UPPERCASE (512M, not 512m)."
|
|
361
|
+
},
|
|
362
|
+
"dns": {
|
|
363
|
+
"status": "ok",
|
|
364
|
+
"maps": "--dns"
|
|
365
|
+
},
|
|
366
|
+
"ulimits": {
|
|
367
|
+
"status": "ok",
|
|
368
|
+
"maps": "--ulimit"
|
|
369
|
+
},
|
|
370
|
+
"domainname": {
|
|
371
|
+
"status": "ok",
|
|
372
|
+
"maps": "--domainname"
|
|
373
|
+
},
|
|
374
|
+
"stop_signal": {
|
|
375
|
+
"status": "ok",
|
|
376
|
+
"maps": "--stop-signal"
|
|
377
|
+
},
|
|
378
|
+
"stop_grace_period": {
|
|
379
|
+
"status": "ok",
|
|
380
|
+
"maps": "--stop-timeout",
|
|
381
|
+
"note": "Converted to whole seconds."
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
"devcontainer": {
|
|
386
|
+
"keys": {
|
|
387
|
+
"image": {
|
|
388
|
+
"status": "ok"
|
|
389
|
+
},
|
|
390
|
+
"build.dockerfile": {
|
|
391
|
+
"status": "ok"
|
|
392
|
+
},
|
|
393
|
+
"build.args": {
|
|
394
|
+
"status": "ok",
|
|
395
|
+
"maps": "--build-arg"
|
|
396
|
+
},
|
|
397
|
+
"build.platform": {
|
|
398
|
+
"status": "missing",
|
|
399
|
+
"note": "Host architecture only."
|
|
400
|
+
},
|
|
401
|
+
"workspaceMount": {
|
|
402
|
+
"status": "partial",
|
|
403
|
+
"note": "VirtioFS; use forward slashes for Windows paths."
|
|
404
|
+
},
|
|
405
|
+
"workspaceFolder": {
|
|
406
|
+
"status": "ok"
|
|
407
|
+
},
|
|
408
|
+
"containerEnv": {
|
|
409
|
+
"status": "ok",
|
|
410
|
+
"maps": "-e"
|
|
411
|
+
},
|
|
412
|
+
"remoteEnv": {
|
|
413
|
+
"status": "ok"
|
|
414
|
+
},
|
|
415
|
+
"forwardPorts": {
|
|
416
|
+
"status": "ok",
|
|
417
|
+
"maps": "-p"
|
|
418
|
+
},
|
|
419
|
+
"postCreateCommand": {
|
|
420
|
+
"status": "ok",
|
|
421
|
+
"maps": "wslc exec"
|
|
422
|
+
},
|
|
423
|
+
"postStartCommand": {
|
|
424
|
+
"status": "ok"
|
|
425
|
+
},
|
|
426
|
+
"postAttachCommand": {
|
|
427
|
+
"status": "ok"
|
|
428
|
+
},
|
|
429
|
+
"remoteUser": {
|
|
430
|
+
"status": "ok",
|
|
431
|
+
"maps": "-u"
|
|
432
|
+
},
|
|
433
|
+
"containerUser": {
|
|
434
|
+
"status": "ok"
|
|
435
|
+
},
|
|
436
|
+
"mounts": {
|
|
437
|
+
"status": "partial",
|
|
438
|
+
"note": "VirtioFS path rules apply."
|
|
439
|
+
},
|
|
440
|
+
"runArgs": {
|
|
441
|
+
"status": "partial",
|
|
442
|
+
"note": "Passed through verbatim; unsupported flags will fail at runtime."
|
|
443
|
+
},
|
|
444
|
+
"dockerComposeFile": {
|
|
445
|
+
"status": "missing",
|
|
446
|
+
"note": "No Compose runtime. Replace with a single image or build entry."
|
|
447
|
+
},
|
|
448
|
+
"features": {
|
|
449
|
+
"status": "missing",
|
|
450
|
+
"note": "Requires BuildKit metadata that wslc build does not emit."
|
|
451
|
+
},
|
|
452
|
+
"runServices": {
|
|
453
|
+
"status": "missing",
|
|
454
|
+
"note": "Compose-only key."
|
|
455
|
+
},
|
|
456
|
+
"shutdownAction": {
|
|
457
|
+
"status": "partial",
|
|
458
|
+
"note": "stopCompose is meaningless without Compose."
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
"requiredSetting": {
|
|
462
|
+
"key": "dev.containers.dockerPath",
|
|
463
|
+
"value": "wslc",
|
|
464
|
+
"note": "Point the Dev Containers extension at wslc. Prefer workspace scope over global."
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
"apiIncompatible": {
|
|
468
|
+
"note": "CLI-driven tooling ports to wslc. API-driven tooling cannot, because wslc is daemonless and exposes no Docker Engine API or socket.",
|
|
469
|
+
"tools": [
|
|
470
|
+
{
|
|
471
|
+
"name": "Testcontainers",
|
|
472
|
+
"via": "Engine API",
|
|
473
|
+
"works": false
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
"name": "Docker-in-Docker sidecars",
|
|
477
|
+
"via": "mounted socket",
|
|
478
|
+
"works": false
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
"name": "Portainer",
|
|
482
|
+
"via": "Engine API",
|
|
483
|
+
"works": false
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
"name": "Lazydocker",
|
|
487
|
+
"via": "Engine API",
|
|
488
|
+
"works": false
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
"name": "LocalStack (docker mode)",
|
|
492
|
+
"via": "Engine API",
|
|
493
|
+
"works": false
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
"name": "act",
|
|
497
|
+
"via": "Engine API",
|
|
498
|
+
"works": false
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
"name": "VS Code Dev Containers",
|
|
502
|
+
"via": "CLI binary",
|
|
503
|
+
"works": true
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
"name": "shell scripts calling docker",
|
|
507
|
+
"via": "CLI binary",
|
|
508
|
+
"works": true
|
|
509
|
+
}
|
|
510
|
+
]
|
|
511
|
+
},
|
|
512
|
+
"links": {
|
|
513
|
+
"site": "https://wslcontainers.com",
|
|
514
|
+
"converter": "https://wslcontainers.com/",
|
|
515
|
+
"cheatsheet": "https://wslcontainers.com/reference/cheatsheet/",
|
|
516
|
+
"compose": "https://wslcontainers.com/guides/compose/",
|
|
517
|
+
"install": "https://wslcontainers.com/guides/install/",
|
|
518
|
+
"vsDocker": "https://wslcontainers.com/guides/vs-docker-desktop/",
|
|
519
|
+
"devcontainers": "https://wslcontainers.com/guides/vscode-dev-containers/",
|
|
520
|
+
"testcontainers": "https://wslcontainers.com/guides/testcontainers/"
|
|
521
|
+
},
|
|
522
|
+
"renamedOptional": {
|
|
523
|
+
"$comment": "wslc accepts BOTH the docker-style short command and the noun-verb form (verified: `wslc ps` and `wslc container list` both exit 0). These rewrites are cosmetic; converters should not treat the short form as an error.",
|
|
524
|
+
"commands": {
|
|
525
|
+
"ps": "container list",
|
|
526
|
+
"images": "image list",
|
|
527
|
+
"rmi": "image remove",
|
|
528
|
+
"rm": "container remove",
|
|
529
|
+
"create": "container create"
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
"verification": {
|
|
533
|
+
"wslcVersion": "2.9.4.0",
|
|
534
|
+
"verifiedAt": "2026-07-29",
|
|
535
|
+
"method": "GitHub Actions windows-2025 runner, real wslc invocations",
|
|
536
|
+
"runs": [
|
|
537
|
+
"30417819781",
|
|
538
|
+
"30417964880",
|
|
539
|
+
"30418758451"
|
|
540
|
+
]
|
|
541
|
+
},
|
|
542
|
+
"twoWord": {
|
|
543
|
+
"system prune": {
|
|
544
|
+
"status": "unsupported",
|
|
545
|
+
"note": "`wslc system` only exposes `session` in 2.9.4 — there is no `system prune`. Run the per-noun prunes instead: `wslc container prune`, `wslc image prune`, `wslc volume prune`, `wslc network prune`."
|
|
546
|
+
},
|
|
547
|
+
"system info": {
|
|
548
|
+
"status": "unsupported",
|
|
549
|
+
"note": "No `system info` in wslc 2.9.4. Use `wslc version`."
|
|
550
|
+
},
|
|
551
|
+
"system df": {
|
|
552
|
+
"status": "unsupported",
|
|
553
|
+
"note": "No `system df` in wslc 2.9.4. Inspect per-noun listings instead."
|
|
554
|
+
},
|
|
555
|
+
"container prune": {
|
|
556
|
+
"status": "supported",
|
|
557
|
+
"note": "Removes all stopped containers."
|
|
558
|
+
},
|
|
559
|
+
"image prune": {
|
|
560
|
+
"status": "supported",
|
|
561
|
+
"note": "Removes dangling images; --all removes all unused images."
|
|
562
|
+
},
|
|
563
|
+
"volume prune": {
|
|
564
|
+
"status": "supported",
|
|
565
|
+
"note": "Removes unused anonymous volumes; --all includes named ones."
|
|
566
|
+
},
|
|
567
|
+
"network prune": {
|
|
568
|
+
"status": "supported",
|
|
569
|
+
"note": "Removes all unused networks."
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
@@ -45,7 +45,12 @@ class Result:
|
|
|
45
45
|
"""0 clean, 1 degraded (flags dropped/rewritten), 2 unmigratable."""
|
|
46
46
|
if self.unsupported_hit or self.compose_hit:
|
|
47
47
|
return 2
|
|
48
|
-
|
|
48
|
+
# severity=error means wslc refuses the command outright (e.g.
|
|
49
|
+
# `--network host` exits 1 with "host mode networking is not
|
|
50
|
+
# supported"), so the result is unmigratable, not merely degraded.
|
|
51
|
+
if any(n.severity == "error" for n in self.notes):
|
|
52
|
+
return 2
|
|
53
|
+
if any(n.severity == "warn" for n in self.notes):
|
|
49
54
|
return 1
|
|
50
55
|
return 0
|
|
51
56
|
|
|
@@ -83,6 +88,11 @@ def tokenize(line: str) -> list[str]:
|
|
|
83
88
|
return out
|
|
84
89
|
|
|
85
90
|
|
|
91
|
+
# Verbs where wslc also accepts the docker-style top-level form, so rewriting to
|
|
92
|
+
# the noun form is cosmetic rather than required. Verified on wslc 2.9.4.
|
|
93
|
+
_ALIAS_OK = set(RULES.get("renamedOptional", {}).get("commands", {}))
|
|
94
|
+
|
|
95
|
+
|
|
86
96
|
def _flag_spec(flag: str) -> tuple[str, dict[str, Any]] | tuple[None, None]:
|
|
87
97
|
flags = RULES["flags"]
|
|
88
98
|
if flag in flags:
|
|
@@ -120,6 +130,15 @@ def _translate_args(args: list[str], notes: list[Note]) -> list[str]:
|
|
|
120
130
|
notes.append(Note(branch.get("severity", "info"), branch["note"]))
|
|
121
131
|
i += consumed
|
|
122
132
|
continue
|
|
133
|
+
if branch["action"] == "error":
|
|
134
|
+
# Value is passed through so the reader sees what they wrote,
|
|
135
|
+
# but flagged as an error: wslc rejects it outright.
|
|
136
|
+
notes.append(Note(branch.get("severity", "error"), branch["note"]))
|
|
137
|
+
out.append(f"{bare}={value}" if has_inline else bare)
|
|
138
|
+
if not has_inline and takes_value and value:
|
|
139
|
+
out.append(value)
|
|
140
|
+
i += consumed
|
|
141
|
+
continue
|
|
123
142
|
notes.append(Note(spec.get("severity", "info"), spec["note"]))
|
|
124
143
|
out.append(f"{bare}={value}" if has_inline else bare)
|
|
125
144
|
if not has_inline and takes_value and value:
|
|
@@ -142,9 +161,30 @@ def _translate_args(args: list[str], notes: list[Note]) -> list[str]:
|
|
|
142
161
|
note_win = spec.get("noteWhenWindowsPath")
|
|
143
162
|
if note_win and (WINDOWS_PATH.match(value) or value.startswith("/mnt/")):
|
|
144
163
|
notes.append(Note(spec.get("severity", "info"), note_win))
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
# Some flags exist but reject certain value formats (verified: wslc wants
|
|
165
|
+
# uppercase size units, so `-m 512m` fails while `512M` works).
|
|
166
|
+
emit_value = value
|
|
167
|
+
fixed = False
|
|
168
|
+
if spec.get("fixValueCase") == "upperSizeSuffix" and value:
|
|
169
|
+
up = re.sub(
|
|
170
|
+
r"^(\d+(?:\.\d+)?)\s*([kmgtKMGT])(b?|B?)$",
|
|
171
|
+
lambda m: m.group(1) + m.group(2).upper() + m.group(3).upper(),
|
|
172
|
+
value,
|
|
173
|
+
)
|
|
174
|
+
if up != value:
|
|
175
|
+
emit_value, fixed = up, True
|
|
176
|
+
if fixed:
|
|
177
|
+
notes.append(Note("warn", spec["noteWhenValueFixed"]))
|
|
178
|
+
else:
|
|
179
|
+
require = spec.get("requireValuePattern")
|
|
180
|
+
if require and emit_value and not re.match(require, emit_value):
|
|
181
|
+
notes.append(Note("error", spec["noteWhenValueRejected"]))
|
|
182
|
+
note_always = spec.get("noteAlways")
|
|
183
|
+
if note_always:
|
|
184
|
+
notes.append(Note(spec.get("severity", "info"), note_always))
|
|
185
|
+
out.append(f"{bare}={emit_value}" if has_inline else arg)
|
|
186
|
+
if not has_inline and takes_value and emit_value:
|
|
187
|
+
out.append(emit_value)
|
|
148
188
|
i += consumed
|
|
149
189
|
return out
|
|
150
190
|
|
|
@@ -193,6 +233,20 @@ def translate_line(raw: str) -> Result | None:
|
|
|
193
233
|
)
|
|
194
234
|
|
|
195
235
|
two = f"{tokens[0]} {tokens[1]}" if len(tokens) > 1 else ""
|
|
236
|
+
# Two-word forms with a verified verdict of their own (e.g. `docker system
|
|
237
|
+
# prune` has no wslc equivalent, but the per-noun prunes do).
|
|
238
|
+
two_word = RULES.get("twoWord", {})
|
|
239
|
+
if two and two in two_word:
|
|
240
|
+
entry = two_word[two]
|
|
241
|
+
if entry["status"] == "unsupported":
|
|
242
|
+
notes.append(Note("error", entry["note"]))
|
|
243
|
+
return Result(
|
|
244
|
+
output=f"# {two}: not supported by wslc", notes=notes, unsupported_hit=True
|
|
245
|
+
)
|
|
246
|
+
notes.append(Note("info", entry["note"]))
|
|
247
|
+
rest = _translate_args(tokens[2:], notes)
|
|
248
|
+
return Result(output=" ".join(["wslc", two, *rest]).strip(), notes=notes)
|
|
249
|
+
|
|
196
250
|
if two and two in (RULES["renamed"].values()):
|
|
197
251
|
rest = _translate_args(tokens[2:], notes)
|
|
198
252
|
return Result(output=" ".join(["wslc", two, *rest]).strip(), notes=notes)
|
|
@@ -216,7 +270,12 @@ def translate_line(raw: str) -> Result | None:
|
|
|
216
270
|
Note(
|
|
217
271
|
"info",
|
|
218
272
|
f"`docker {verb}` is grouped under a noun in wslc: `wslc {mapped}`. "
|
|
219
|
-
|
|
273
|
+
+ (
|
|
274
|
+
f"`wslc {verb}` also works as a top-level alias (verified on 2.9.4), "
|
|
275
|
+
"so this rewrite is cosmetic."
|
|
276
|
+
if verb in _ALIAS_OK
|
|
277
|
+
else f"There is no top-level `wslc {verb}`; the noun form is required."
|
|
278
|
+
),
|
|
220
279
|
)
|
|
221
280
|
)
|
|
222
281
|
return Result(output=" ".join(["wslc", mapped, *rest]).strip(), notes=notes)
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$comment": "Single source of truth for Docker -> wslc translation. Consumed by the Python package, the npm CLI, the MCP server and the VS Code extension. Do not fork this logic per language.",
|
|
3
|
-
"schemaVersion": 1,
|
|
4
|
-
"wslcPreview": "2026-07",
|
|
5
|
-
"identical": [
|
|
6
|
-
"run", "build", "pull", "push", "stop", "start", "restart", "exec", "logs",
|
|
7
|
-
"inspect", "stats", "cp", "tag", "login", "logout", "kill", "pause",
|
|
8
|
-
"unpause", "version", "info", "top", "wait", "port", "rename", "diff",
|
|
9
|
-
"commit", "save", "load", "attach"
|
|
10
|
-
],
|
|
11
|
-
"renamed": {
|
|
12
|
-
"ps": "container list",
|
|
13
|
-
"images": "image list",
|
|
14
|
-
"rmi": "image remove",
|
|
15
|
-
"rm": "container remove",
|
|
16
|
-
"create": "container create",
|
|
17
|
-
"history": "image history",
|
|
18
|
-
"search": "image search"
|
|
19
|
-
},
|
|
20
|
-
"renamedFlags": {
|
|
21
|
-
"ps": { "-a": "--all", "--all": "--all", "-q": "--quiet", "--quiet": "--quiet" },
|
|
22
|
-
"images": { "-a": "--all", "--all": "--all" },
|
|
23
|
-
"rm": { "-f": "--force", "--force": "--force" },
|
|
24
|
-
"rmi": { "-f": "--force", "--force": "--force" }
|
|
25
|
-
},
|
|
26
|
-
"unsupported": {
|
|
27
|
-
"swarm": "Swarm orchestration has no wslc equivalent. Use Kubernetes or keep Docker for this workload.",
|
|
28
|
-
"service": "Swarm services are not supported by wslc.",
|
|
29
|
-
"stack": "Swarm stacks are not supported by wslc.",
|
|
30
|
-
"node": "Swarm node management is not supported by wslc.",
|
|
31
|
-
"secret": "Swarm secrets are not supported. Pass values with -e or mount a file.",
|
|
32
|
-
"config": "Swarm configs are not supported by wslc.",
|
|
33
|
-
"plugin": "Docker plugins are not supported by wslc.",
|
|
34
|
-
"context": "wslc has a single local runtime, so there are no contexts to switch.",
|
|
35
|
-
"buildx": "buildx / bake is not available. Use wslc build for single-platform images.",
|
|
36
|
-
"manifest": "Manifest lists cannot be created with wslc in the preview.",
|
|
37
|
-
"trust": "Docker Content Trust is Docker-specific and has no wslc equivalent.",
|
|
38
|
-
"checkpoint": "Checkpoint/restore is not implemented in wslc."
|
|
39
|
-
},
|
|
40
|
-
"flags": {
|
|
41
|
-
"--platform": {
|
|
42
|
-
"action": "drop",
|
|
43
|
-
"takesValue": true,
|
|
44
|
-
"severity": "warn",
|
|
45
|
-
"note": "`--platform` is dropped: wslc builds and runs for the host architecture only in the preview."
|
|
46
|
-
},
|
|
47
|
-
"--restart": {
|
|
48
|
-
"action": "drop",
|
|
49
|
-
"takesValue": true,
|
|
50
|
-
"severity": "warn",
|
|
51
|
-
"note": "Restart policies are not implemented in the wslc preview. Flag dropped — use a Windows scheduled task or a wrapper script for auto-restart."
|
|
52
|
-
},
|
|
53
|
-
"--gpus": {
|
|
54
|
-
"action": "rewrite",
|
|
55
|
-
"takesValue": true,
|
|
56
|
-
"replaceWith": "--device nvidia.com/gpu=all",
|
|
57
|
-
"severity": "info",
|
|
58
|
-
"note": "GPU access in wslc goes through the Container Device Interface. Use `--device nvidia.com/gpu=all` instead of `--gpus`."
|
|
59
|
-
},
|
|
60
|
-
"--network": {
|
|
61
|
-
"action": "conditional",
|
|
62
|
-
"takesValue": true,
|
|
63
|
-
"whenValue": {
|
|
64
|
-
"host": {
|
|
65
|
-
"action": "drop",
|
|
66
|
-
"severity": "info",
|
|
67
|
-
"note": "`--network host` behaves differently: wslc routes container traffic through the Windows host stack already, so host networking is implicit. Flag dropped."
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
"severity": "info",
|
|
71
|
-
"note": "Custom networks are supported but bridge-only. Create it first with `wslc network create`."
|
|
72
|
-
},
|
|
73
|
-
"--volume": {
|
|
74
|
-
"action": "keep",
|
|
75
|
-
"takesValue": true,
|
|
76
|
-
"aliases": ["-v", "--mount"],
|
|
77
|
-
"severity": "info",
|
|
78
|
-
"noteWhenWindowsPath": "Windows host paths are shared over VirtioFS. Use forward slashes (`C:/work:/app`) and expect Linux-style permissions on the container side."
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
"compose": {
|
|
82
|
-
"supported": false,
|
|
83
|
-
"note": "wslc has no built-in Compose runtime. Translate each service into a wslc run call, or drive them from a PowerShell/bash script.",
|
|
84
|
-
"docsUrl": "https://wslcontainers.com/guides/compose/",
|
|
85
|
-
"keys": {
|
|
86
|
-
"image": { "status": "ok", "maps": "positional image argument" },
|
|
87
|
-
"build": { "status": "ok", "maps": "wslc build -t <name>" },
|
|
88
|
-
"command": { "status": "ok", "maps": "trailing command args" },
|
|
89
|
-
"entrypoint": { "status": "ok", "maps": "--entrypoint" },
|
|
90
|
-
"environment": { "status": "ok", "maps": "-e KEY=VALUE" },
|
|
91
|
-
"env_file": { "status": "ok", "maps": "--env-file" },
|
|
92
|
-
"ports": { "status": "ok", "maps": "-p HOST:CONTAINER" },
|
|
93
|
-
"volumes": { "status": "partial", "maps": "-v", "note": "Named volumes must be created first with wslc volume create. Windows paths go over VirtioFS." },
|
|
94
|
-
"networks": { "status": "partial", "maps": "--network", "note": "Bridge-only. Create with wslc network create before running." },
|
|
95
|
-
"working_dir": { "status": "ok", "maps": "-w" },
|
|
96
|
-
"user": { "status": "ok", "maps": "-u" },
|
|
97
|
-
"hostname": { "status": "ok", "maps": "--hostname" },
|
|
98
|
-
"container_name": { "status": "ok", "maps": "--name" },
|
|
99
|
-
"labels": { "status": "ok", "maps": "--label" },
|
|
100
|
-
"cap_add": { "status": "partial", "maps": "--cap-add", "note": "Some capabilities are restricted inside the utility VM." },
|
|
101
|
-
"devices": { "status": "partial", "maps": "--device", "note": "CDI syntax differs from Docker for GPUs." },
|
|
102
|
-
"tmpfs": { "status": "ok", "maps": "--tmpfs" },
|
|
103
|
-
"stdin_open": { "status": "ok", "maps": "-i" },
|
|
104
|
-
"tty": { "status": "ok", "maps": "-t" },
|
|
105
|
-
"restart": { "status": "missing", "note": "No restart policies in the wslc preview." },
|
|
106
|
-
"healthcheck": { "status": "missing", "note": "wslc has no healthcheck support; depends_on conditions relying on it cannot work." },
|
|
107
|
-
"depends_on": { "status": "missing", "note": "No dependency ordering. Start services in order yourself and add readiness waits." },
|
|
108
|
-
"deploy": { "status": "missing", "note": "Swarm-only section, ignored entirely." },
|
|
109
|
-
"profiles": { "status": "missing", "note": "Compose-only concept." },
|
|
110
|
-
"extends": { "status": "missing", "note": "Compose-only concept." },
|
|
111
|
-
"configs": { "status": "missing", "note": "Swarm configs are unsupported; mount a file instead." },
|
|
112
|
-
"secrets": { "status": "missing", "note": "Swarm secrets are unsupported; use -e or a mounted file." }
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
"devcontainer": {
|
|
116
|
-
"keys": {
|
|
117
|
-
"image": { "status": "ok" },
|
|
118
|
-
"build.dockerfile": { "status": "ok" },
|
|
119
|
-
"build.args": { "status": "ok", "maps": "--build-arg" },
|
|
120
|
-
"build.platform": { "status": "missing", "note": "Host architecture only." },
|
|
121
|
-
"workspaceMount": { "status": "partial", "note": "VirtioFS; use forward slashes for Windows paths." },
|
|
122
|
-
"workspaceFolder": { "status": "ok" },
|
|
123
|
-
"containerEnv": { "status": "ok", "maps": "-e" },
|
|
124
|
-
"remoteEnv": { "status": "ok" },
|
|
125
|
-
"forwardPorts": { "status": "ok", "maps": "-p" },
|
|
126
|
-
"postCreateCommand": { "status": "ok", "maps": "wslc exec" },
|
|
127
|
-
"postStartCommand": { "status": "ok" },
|
|
128
|
-
"postAttachCommand": { "status": "ok" },
|
|
129
|
-
"remoteUser": { "status": "ok", "maps": "-u" },
|
|
130
|
-
"containerUser": { "status": "ok" },
|
|
131
|
-
"mounts": { "status": "partial", "note": "VirtioFS path rules apply." },
|
|
132
|
-
"runArgs": { "status": "partial", "note": "Passed through verbatim; unsupported flags will fail at runtime." },
|
|
133
|
-
"dockerComposeFile": { "status": "missing", "note": "No Compose runtime. Replace with a single image or build entry." },
|
|
134
|
-
"features": { "status": "missing", "note": "Requires BuildKit metadata that wslc build does not emit." },
|
|
135
|
-
"runServices": { "status": "missing", "note": "Compose-only key." },
|
|
136
|
-
"shutdownAction": { "status": "partial", "note": "stopCompose is meaningless without Compose." }
|
|
137
|
-
},
|
|
138
|
-
"requiredSetting": {
|
|
139
|
-
"key": "dev.containers.dockerPath",
|
|
140
|
-
"value": "wslc",
|
|
141
|
-
"note": "Point the Dev Containers extension at wslc. Prefer workspace scope over global."
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
"apiIncompatible": {
|
|
145
|
-
"note": "CLI-driven tooling ports to wslc. API-driven tooling cannot, because wslc is daemonless and exposes no Docker Engine API or socket.",
|
|
146
|
-
"tools": [
|
|
147
|
-
{ "name": "Testcontainers", "via": "Engine API", "works": false },
|
|
148
|
-
{ "name": "Docker-in-Docker sidecars", "via": "mounted socket", "works": false },
|
|
149
|
-
{ "name": "Portainer", "via": "Engine API", "works": false },
|
|
150
|
-
{ "name": "Lazydocker", "via": "Engine API", "works": false },
|
|
151
|
-
{ "name": "LocalStack (docker mode)", "via": "Engine API", "works": false },
|
|
152
|
-
{ "name": "act", "via": "Engine API", "works": false },
|
|
153
|
-
{ "name": "VS Code Dev Containers", "via": "CLI binary", "works": true },
|
|
154
|
-
{ "name": "shell scripts calling docker", "via": "CLI binary", "works": true }
|
|
155
|
-
]
|
|
156
|
-
},
|
|
157
|
-
"links": {
|
|
158
|
-
"site": "https://wslcontainers.com",
|
|
159
|
-
"converter": "https://wslcontainers.com/",
|
|
160
|
-
"cheatsheet": "https://wslcontainers.com/reference/cheatsheet/",
|
|
161
|
-
"compose": "https://wslcontainers.com/guides/compose/",
|
|
162
|
-
"install": "https://wslcontainers.com/guides/install/",
|
|
163
|
-
"vsDocker": "https://wslcontainers.com/guides/vs-docker-desktop/",
|
|
164
|
-
"devcontainers": "https://wslcontainers.com/guides/vscode-dev-containers/",
|
|
165
|
-
"testcontainers": "https://wslcontainers.com/guides/testcontainers/"
|
|
166
|
-
}
|
|
167
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|