@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.
@@ -6,14 +6,18 @@
6
6
  "asset": "assets/bootstrap-migrations/wsl-setup/setup.sh",
7
7
  "source": {
8
8
  "component": "bootstrap",
9
- "revision": "173203baaf36935d04eb81c89f1d016131a40442",
9
+ "revision": "1e656d533a259428bbfb21aada548429462ba190",
10
10
  "artifact": "templates/scripts/setup.sh.tpl"
11
11
  },
12
- "targetGitBlob": "4e8c48a3d33c6a8ae4534ec860c2ee0205e417aa",
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": "9f0f7fd4e0e2220250f6fd5b035706a7d15834af",
9
+ "revision": "043b3b2a0e3ba03b8d18666bb3fef59c588bfa16",
10
10
  "artifact": "runtime/dev_environment_wsl.py"
11
11
  },
12
- "targetGitBlob": "883f7bbadd2de4bc0dcb601ee2285f88a0085b52",
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 [[ " ${*:-} " == *" --check "* ]]; then
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
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@mriekki/project-forge",
4
- "version": "4.14.2",
4
+ "version": "4.15.0",
5
5
  "defaultProfile": "greenfield-project",
6
6
  "profiles": {
7
7
  "greenfield-project": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@mriekki/project-forge",
4
- "version": "4.14.2",
4
+ "version": "4.15.0",
5
5
  "components": {
6
6
  "core": {
7
7
  "revision": "b51cb62bcc9630748168c9fd1900c6109cafb587",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mriekki/project-forge",
3
- "version": "4.14.2",
3
+ "version": "4.15.0",
4
4
  "description": "Public consumer distribution and updater for Project Forge engineering assets.",
5
5
  "type": "module",
6
6
  "bin": {