@mriekki/project-forge 4.14.2 → 4.15.0
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.
- package/assets/bootstrap-migrations/wsl-setup/setup-launcher.manifest.json +8 -4
- package/assets/bootstrap-migrations/wsl-setup/setup-wsl.manifest.json +6 -4
- package/assets/bootstrap-migrations/wsl-setup/setup-wsl.py +87 -10
- package/assets/bootstrap-migrations/wsl-setup/setup.sh +104 -1
- package/assets/components.json +1 -1
- package/assets/generated/ASSEMBLY.json +1 -1
- package/package.json +1 -1
|
@@ -6,14 +6,18 @@
|
|
|
6
6
|
"asset": "assets/bootstrap-migrations/wsl-setup/setup.sh",
|
|
7
7
|
"source": {
|
|
8
8
|
"component": "bootstrap",
|
|
9
|
-
"revision": "
|
|
9
|
+
"revision": "1e656d533a259428bbfb21aada548429462ba190",
|
|
10
10
|
"artifact": "templates/scripts/setup.sh.tpl"
|
|
11
11
|
},
|
|
12
|
-
"targetGitBlob": "
|
|
12
|
+
"targetGitBlob": "f2555c464709492941007b838b9cc86051a61277",
|
|
13
13
|
"supportedPredecessorGitBlobs": [
|
|
14
|
-
"ec941c079da066785ac628d6bbb7dd6b668494f2"
|
|
14
|
+
"ec941c079da066785ac628d6bbb7dd6b668494f2",
|
|
15
|
+
"4e8c48a3d33c6a8ae4534ec860c2ee0205e417aa"
|
|
15
16
|
],
|
|
16
17
|
"supportedBootstrapRevisions": [
|
|
17
|
-
"0c45eb332a815ad79ea061443f337500b9dc5712"
|
|
18
|
+
"0c45eb332a815ad79ea061443f337500b9dc5712",
|
|
19
|
+
"173203baaf36935d04eb81c89f1d016131a40442",
|
|
20
|
+
"9f0f7fd4e0e2220250f6fd5b035706a7d15834af",
|
|
21
|
+
"043b3b2a0e3ba03b8d18666bb3fef59c588bfa16"
|
|
18
22
|
]
|
|
19
23
|
}
|
|
@@ -6,16 +6,18 @@
|
|
|
6
6
|
"asset": "assets/bootstrap-migrations/wsl-setup/setup-wsl.py",
|
|
7
7
|
"source": {
|
|
8
8
|
"component": "bootstrap",
|
|
9
|
-
"revision": "
|
|
9
|
+
"revision": "043b3b2a0e3ba03b8d18666bb3fef59c588bfa16",
|
|
10
10
|
"artifact": "runtime/dev_environment_wsl.py"
|
|
11
11
|
},
|
|
12
|
-
"targetGitBlob": "
|
|
12
|
+
"targetGitBlob": "0b959b4e85508016f6e8c12d403a94b2eed46c1f",
|
|
13
13
|
"supportedPredecessorGitBlobs": [
|
|
14
|
-
"6731910246fb99887c58b9f3a8a14fec1bf02c01"
|
|
14
|
+
"6731910246fb99887c58b9f3a8a14fec1bf02c01",
|
|
15
|
+
"883f7bbadd2de4bc0dcb601ee2285f88a0085b52"
|
|
15
16
|
],
|
|
16
17
|
"supportedBootstrapRevisions": [
|
|
17
18
|
"0c45eb332a815ad79ea061443f337500b9dc5712",
|
|
18
|
-
"173203baaf36935d04eb81c89f1d016131a40442"
|
|
19
|
+
"173203baaf36935d04eb81c89f1d016131a40442",
|
|
20
|
+
"9f0f7fd4e0e2220250f6fd5b035706a7d15834af"
|
|
19
21
|
],
|
|
20
22
|
"allowMissing": true
|
|
21
23
|
}
|
|
@@ -8,6 +8,7 @@ import re
|
|
|
8
8
|
import shutil
|
|
9
9
|
import subprocess
|
|
10
10
|
import sys
|
|
11
|
+
import tempfile
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
from typing import Any
|
|
13
14
|
|
|
@@ -160,6 +161,90 @@ def ensure_fnm_node(version: str | None, check_only: bool) -> tuple[bool, str, s
|
|
|
160
161
|
return ok, text or f"Node {version} verification failed", "PASS" if ok else "FAILED"
|
|
161
162
|
|
|
162
163
|
|
|
164
|
+
def global_dotnet_sdk_version() -> str | None:
|
|
165
|
+
global_json = ROOT / "global.json"
|
|
166
|
+
if not global_json.exists():
|
|
167
|
+
return None
|
|
168
|
+
sdk = read_json(global_json).get("sdk")
|
|
169
|
+
if not isinstance(sdk, dict):
|
|
170
|
+
return None
|
|
171
|
+
version = sdk.get("version")
|
|
172
|
+
return version if isinstance(version, str) else None
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def ensure_dotnet_sdk(
|
|
176
|
+
spec: dict[str, Any],
|
|
177
|
+
version: str | None,
|
|
178
|
+
check_only: bool,
|
|
179
|
+
) -> tuple[bool, str, str]:
|
|
180
|
+
ok, detail = version_ok(spec, version)
|
|
181
|
+
if ok:
|
|
182
|
+
return True, detail, "PASS"
|
|
183
|
+
if check_only:
|
|
184
|
+
return False, detail, "MISSING" if detail == "missing" else "VERSION_MISMATCH"
|
|
185
|
+
|
|
186
|
+
if not version:
|
|
187
|
+
return False, ".NET SDK version is required for automatic WSL installation", "FAILED"
|
|
188
|
+
major = version.split(".", 1)[0]
|
|
189
|
+
if not re.fullmatch(r"\d+", major):
|
|
190
|
+
return False, f"Unsupported .NET SDK version: {version}", "FAILED"
|
|
191
|
+
|
|
192
|
+
ensure_apt_package(f"dotnet-sdk-{major}.0")
|
|
193
|
+
ok, detail = version_ok(spec, version)
|
|
194
|
+
if ok:
|
|
195
|
+
return True, detail, "PASS"
|
|
196
|
+
|
|
197
|
+
sdk_version = global_dotnet_sdk_version()
|
|
198
|
+
if not sdk_version:
|
|
199
|
+
return False, (
|
|
200
|
+
detail
|
|
201
|
+
or f".NET SDK {version} is installed, but the repository resolver still cannot select a compatible SDK"
|
|
202
|
+
), "FAILED"
|
|
203
|
+
if not re.fullmatch(r"\d+\.\d+\.\d+", sdk_version):
|
|
204
|
+
return False, f"Unsupported global.json .NET SDK version: {sdk_version}", "FAILED"
|
|
205
|
+
|
|
206
|
+
declared_parts = version.split(".")
|
|
207
|
+
sdk_parts = sdk_version.split(".")
|
|
208
|
+
if sdk_parts[: len(declared_parts)] != declared_parts:
|
|
209
|
+
return False, (
|
|
210
|
+
f"global.json requires .NET SDK {sdk_version}, outside the declared setup family {version}"
|
|
211
|
+
), "FAILED"
|
|
212
|
+
|
|
213
|
+
dotnet = command_path("dotnet")
|
|
214
|
+
if not dotnet:
|
|
215
|
+
return False, "dotnet remained unavailable after Ubuntu SDK installation", "FAILED"
|
|
216
|
+
install_root = Path(dotnet).resolve().parent
|
|
217
|
+
|
|
218
|
+
ensure_apt_package("curl")
|
|
219
|
+
with tempfile.TemporaryDirectory(prefix="project-forge-dotnet-") as temp_dir:
|
|
220
|
+
installer = Path(temp_dir) / "dotnet-install.sh"
|
|
221
|
+
download = run(
|
|
222
|
+
["curl", "-fsSL", "https://dot.net/v1/dotnet-install.sh", "-o", str(installer)],
|
|
223
|
+
capture=False,
|
|
224
|
+
)
|
|
225
|
+
if download.returncode != 0:
|
|
226
|
+
return False, "Unable to download the official .NET install script", "FAILED"
|
|
227
|
+
|
|
228
|
+
install = run(
|
|
229
|
+
[
|
|
230
|
+
"sudo",
|
|
231
|
+
"bash",
|
|
232
|
+
str(installer),
|
|
233
|
+
"--version",
|
|
234
|
+
sdk_version,
|
|
235
|
+
"--install-dir",
|
|
236
|
+
str(install_root),
|
|
237
|
+
"--skip-non-versioned-files",
|
|
238
|
+
],
|
|
239
|
+
capture=False,
|
|
240
|
+
)
|
|
241
|
+
if install.returncode != 0:
|
|
242
|
+
return False, f"Unable to install .NET SDK {sdk_version}", "FAILED"
|
|
243
|
+
|
|
244
|
+
ok, detail = version_ok(spec, version)
|
|
245
|
+
return ok, detail, "PASS" if ok else "FAILED"
|
|
246
|
+
|
|
247
|
+
|
|
163
248
|
def ensure_tool(entry: dict[str, Any], check_only: bool) -> tuple[bool, str, str]:
|
|
164
249
|
tool_id = entry["id"]
|
|
165
250
|
spec = read_json(CATALOG / f"{tool_id}.json")
|
|
@@ -169,6 +254,8 @@ def ensure_tool(entry: dict[str, Any], check_only: bool) -> tuple[bool, str, str
|
|
|
169
254
|
return ensure_fnm_available(check_only)
|
|
170
255
|
if tool_id == "node":
|
|
171
256
|
return ensure_fnm_node(version, check_only)
|
|
257
|
+
if tool_id == "dotnet":
|
|
258
|
+
return ensure_dotnet_sdk(spec, version, check_only)
|
|
172
259
|
|
|
173
260
|
ok, detail = version_ok(spec, version)
|
|
174
261
|
if ok:
|
|
@@ -205,16 +292,6 @@ def ensure_tool(entry: dict[str, Any], check_only: bool) -> tuple[bool, str, str
|
|
|
205
292
|
ok, detail = version_ok(spec, version)
|
|
206
293
|
return ok, detail, "PASS" if ok else "FAILED"
|
|
207
294
|
|
|
208
|
-
if tool_id == "dotnet":
|
|
209
|
-
if not version:
|
|
210
|
-
return False, ".NET SDK version is required for automatic WSL installation", "FAILED"
|
|
211
|
-
major = version.split(".", 1)[0]
|
|
212
|
-
if not re.fullmatch(r"\d+", major):
|
|
213
|
-
return False, f"Unsupported .NET SDK version: {version}", "FAILED"
|
|
214
|
-
ensure_apt_package(f"dotnet-sdk-{major}.0")
|
|
215
|
-
ok, detail = version_ok(spec, version)
|
|
216
|
-
return ok, detail, "PASS" if ok else "FAILED"
|
|
217
|
-
|
|
218
295
|
if tool_id == "vscode":
|
|
219
296
|
return False, (
|
|
220
297
|
"VS Code's WSL command bridge is unavailable. Install VS Code on Windows and the "
|
|
@@ -11,9 +11,17 @@ if [[ "$(/usr/bin/uname -s)" == "Linux" ]]; then
|
|
|
11
11
|
exit 2
|
|
12
12
|
fi
|
|
13
13
|
|
|
14
|
+
CHECK_ONLY=0
|
|
15
|
+
for argument in "$@"; do
|
|
16
|
+
if [[ "$argument" == "--check" ]]; then
|
|
17
|
+
CHECK_ONLY=1
|
|
18
|
+
break
|
|
19
|
+
fi
|
|
20
|
+
done
|
|
21
|
+
|
|
14
22
|
PYTHON_BIN="$(command -v python3 || true)"
|
|
15
23
|
if [[ -z "$PYTHON_BIN" ]]; then
|
|
16
|
-
if [[ "
|
|
24
|
+
if [[ "$CHECK_ONLY" -eq 1 ]]; then
|
|
17
25
|
echo "ERROR: python3 is required to run the read-only Project Forge WSL2 setup check." >&2
|
|
18
26
|
exit 2
|
|
19
27
|
fi
|
|
@@ -27,6 +35,101 @@ if [[ "$(/usr/bin/uname -s)" == "Linux" ]]; then
|
|
|
27
35
|
fi
|
|
28
36
|
fi
|
|
29
37
|
|
|
38
|
+
RTK_DECLARED="$($PYTHON_BIN - "$ROOT/DEV_ENVIRONMENT.json" <<'PY'
|
|
39
|
+
import json
|
|
40
|
+
import sys
|
|
41
|
+
from pathlib import Path
|
|
42
|
+
|
|
43
|
+
config = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
|
44
|
+
print("yes" if any(entry.get("id") == "rtk" for entry in config.get("tools", [])) else "no")
|
|
45
|
+
PY
|
|
46
|
+
)"
|
|
47
|
+
|
|
48
|
+
if [[ "$RTK_DECLARED" == "yes" ]]; then
|
|
49
|
+
RTK_VERSION="1.1.0"
|
|
50
|
+
RTK_INSTALL_DIR="$HOME/.local/bin"
|
|
51
|
+
export PATH="$RTK_INSTALL_DIR:$PATH"
|
|
52
|
+
|
|
53
|
+
rtk_is_token_killer() {
|
|
54
|
+
command -v rtk >/dev/null 2>&1 && rtk --version >/dev/null 2>&1 && rtk gain >/dev/null 2>&1
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
install_rtk_wsl() {
|
|
58
|
+
local arch asset expected archive actual extracted temp_dir
|
|
59
|
+
arch="$(/usr/bin/uname -m)"
|
|
60
|
+
case "$arch" in
|
|
61
|
+
x86_64|amd64)
|
|
62
|
+
asset="rtk-x86_64-unknown-linux-musl.tar.gz"
|
|
63
|
+
expected="9454ad1b9489daa57ab0a9f4ebe2faaf4dc82b8a0f26c9e5135151abd7c9378d"
|
|
64
|
+
;;
|
|
65
|
+
aarch64|arm64)
|
|
66
|
+
asset="rtk-aarch64-unknown-linux-gnu.tar.gz"
|
|
67
|
+
expected="a5c00c3ab0129884bcb757d348afc1a650efa0f4f0769da97c341f28f89719ed"
|
|
68
|
+
;;
|
|
69
|
+
*)
|
|
70
|
+
echo "ERROR: RTK ${RTK_VERSION} has no Project Forge-pinned WSL asset for architecture: $arch" >&2
|
|
71
|
+
return 2
|
|
72
|
+
;;
|
|
73
|
+
esac
|
|
74
|
+
|
|
75
|
+
if ! command -v curl >/dev/null 2>&1; then
|
|
76
|
+
sudo apt-get update
|
|
77
|
+
sudo apt-get install -y curl
|
|
78
|
+
fi
|
|
79
|
+
if ! command -v tar >/dev/null 2>&1; then
|
|
80
|
+
sudo apt-get update
|
|
81
|
+
sudo apt-get install -y tar
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
temp_dir="$(/usr/bin/mktemp -d "${TMPDIR:-/tmp}/project-forge-rtk.XXXXXX")"
|
|
85
|
+
archive="$temp_dir/$asset"
|
|
86
|
+
if ! curl -fsSL "https://github.com/rtk-ai/rtk/releases/download/v${RTK_VERSION}/${asset}" -o "$archive"; then
|
|
87
|
+
/bin/rm -rf "$temp_dir"
|
|
88
|
+
echo "ERROR: unable to download pinned RTK ${RTK_VERSION} release asset." >&2
|
|
89
|
+
return 2
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
actual="$(/usr/bin/sha256sum "$archive" | /usr/bin/awk '{print $1}')"
|
|
93
|
+
if [[ "$actual" != "$expected" ]]; then
|
|
94
|
+
/bin/rm -rf "$temp_dir"
|
|
95
|
+
echo "ERROR: RTK ${RTK_VERSION} release checksum mismatch." >&2
|
|
96
|
+
echo "Expected $expected but got $actual." >&2
|
|
97
|
+
return 2
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
if ! /bin/tar -xzf "$archive" -C "$temp_dir"; then
|
|
101
|
+
/bin/rm -rf "$temp_dir"
|
|
102
|
+
echo "ERROR: unable to extract pinned RTK ${RTK_VERSION} release asset." >&2
|
|
103
|
+
return 2
|
|
104
|
+
fi
|
|
105
|
+
extracted="$(/usr/bin/find "$temp_dir" -type f -name rtk -print -quit)"
|
|
106
|
+
if [[ -z "$extracted" ]]; then
|
|
107
|
+
/bin/rm -rf "$temp_dir"
|
|
108
|
+
echo "ERROR: pinned RTK ${RTK_VERSION} release asset did not contain the rtk binary." >&2
|
|
109
|
+
return 2
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
/bin/mkdir -p "$RTK_INSTALL_DIR"
|
|
113
|
+
/usr/bin/install -m 0755 "$extracted" "$RTK_INSTALL_DIR/rtk"
|
|
114
|
+
/bin/rm -rf "$temp_dir"
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if ! rtk_is_token_killer; then
|
|
118
|
+
if [[ "$CHECK_ONLY" -eq 0 ]]; then
|
|
119
|
+
echo "Project Forge: installing pinned RTK ${RTK_VERSION} for token-efficient AI shell output."
|
|
120
|
+
install_rtk_wsl
|
|
121
|
+
fi
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
if rtk_is_token_killer && [[ "$CHECK_ONLY" -eq 0 ]]; then
|
|
125
|
+
echo "Project Forge: configuring RTK global GitHub Copilot command rewriting."
|
|
126
|
+
if ! RTK_TELEMETRY_DISABLED=1 rtk init -g --copilot --auto-patch --no-trust-filters; then
|
|
127
|
+
echo "ERROR: RTK is installed but GitHub Copilot integration could not be configured." >&2
|
|
128
|
+
exit 2
|
|
129
|
+
fi
|
|
130
|
+
fi
|
|
131
|
+
fi
|
|
132
|
+
|
|
30
133
|
exec "$PYTHON_BIN" "$WSL_SETUP_ENGINE" "$@"
|
|
31
134
|
fi
|
|
32
135
|
|
package/assets/components.json
CHANGED