agent-dev-env 0.1.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/LICENSE +21 -0
- package/dist/assets/bridge/bridge.js +202 -0
- package/dist/assets/guest/guest-agent-mac.js +586 -0
- package/dist/assets/guest/guest-agent-ubuntu.js +481 -0
- package/dist/assets/guest/guest-agent-windows.js +466 -0
- package/dist/assets/images/mac/CHANGELOG.md +265 -0
- package/dist/assets/images/mac/README.md +63 -0
- package/dist/assets/images/mac/sandbox.pkr.hcl +468 -0
- package/dist/assets/images/mac/vars/sandbox-macos-tahoe.pkrvars.hcl +39 -0
- package/dist/assets/images/ubuntu-arm64-vmware/CHANGELOG.md +239 -0
- package/dist/assets/images/ubuntu-arm64-vmware/README.md +177 -0
- package/dist/assets/images/ubuntu-arm64-vmware/autoinstall/meta-data +2 -0
- package/dist/assets/images/ubuntu-arm64-vmware/autoinstall/user-data +34 -0
- package/dist/assets/images/ubuntu-arm64-vmware/sandbox.pkr.hcl +776 -0
- package/dist/assets/images/ubuntu-arm64-vmware/vars/sandbox-ubuntu-24-04-arm64-vmware.pkrvars.hcl +71 -0
- package/dist/assets/images/windows-arm64-qemu/CHANGELOG.md +209 -0
- package/dist/assets/images/windows-arm64-qemu/README.md +142 -0
- package/dist/assets/images/windows-arm64-qemu/autounattend.xml +325 -0
- package/dist/assets/images/windows-arm64-qemu/qemu-with-tpm.sh +170 -0
- package/dist/assets/images/windows-arm64-qemu/sandbox.pkr.hcl +1012 -0
- package/dist/assets/images/windows-arm64-qemu/vars/sandbox-windows-11-arm64-qemu.pkrvars.hcl +83 -0
- package/dist/assets/images/windows-arm64-vmware/CHANGELOG.md +270 -0
- package/dist/assets/images/windows-arm64-vmware/README.md +171 -0
- package/dist/assets/images/windows-arm64-vmware/autounattend.xml +311 -0
- package/dist/assets/images/windows-arm64-vmware/sandbox.pkr.hcl +1036 -0
- package/dist/assets/images/windows-arm64-vmware/vars/sandbox-windows-11-arm64-vmware.pkrvars.hcl +83 -0
- package/dist/assets/rules/agent-rules-linux.md +68 -0
- package/dist/assets/rules/agent-rules.md +71 -0
- package/dist/assets/watchdog/watch-build-ocr.swift +40 -0
- package/dist/assets/watchdog/watch-build.py +275 -0
- package/dist/cli.js +52 -0
- package/dist/commands/delete.js +179 -0
- package/dist/commands/doctor.js +165 -0
- package/dist/commands/list.js +38 -0
- package/dist/commands/not-yet.js +15 -0
- package/dist/commands/register.js +150 -0
- package/dist/commands/run.js +65 -0
- package/dist/commands/status.js +142 -0
- package/dist/commands/stop.js +186 -0
- package/dist/commands/sync.js +98 -0
- package/dist/lib/exec.js +250 -0
- package/dist/lib/ghcr.js +82 -0
- package/dist/lib/git.js +100 -0
- package/dist/lib/logger.js +87 -0
- package/dist/lib/network.js +63 -0
- package/dist/lib/paths.js +116 -0
- package/dist/lib/platform.js +87 -0
- package/dist/lib/prompt.js +66 -0
- package/dist/lib/qemu.js +373 -0
- package/dist/lib/regex.js +9 -0
- package/dist/lib/ssh.js +230 -0
- package/dist/lib/tart.js +210 -0
- package/dist/lib/template.js +35 -0
- package/dist/lib/vars.js +99 -0
- package/dist/lib/vmrun.js +334 -0
- package/dist/lifecycle/build-macos.js +23 -0
- package/dist/lifecycle/build-qemu.js +204 -0
- package/dist/lifecycle/build-shared.js +355 -0
- package/dist/lifecycle/build-ubuntu.js +184 -0
- package/dist/lifecycle/build-watchdog.js +146 -0
- package/dist/lifecycle/build-windows-vmware.js +112 -0
- package/dist/lifecycle/build.js +55 -0
- package/dist/lifecycle/catalog.js +165 -0
- package/dist/lifecycle/deploy.js +161 -0
- package/dist/lifecycle/tag.js +77 -0
- package/dist/lifecycle/watch-build.js +64 -0
- package/dist/runners/bridges.js +153 -0
- package/dist/runners/framework.js +66 -0
- package/dist/runners/macos-bridges.js +143 -0
- package/dist/runners/macos-guest.js +76 -0
- package/dist/runners/macos-rules.js +77 -0
- package/dist/runners/macos-summary.js +145 -0
- package/dist/runners/macos.js +258 -0
- package/dist/runners/openchamber.js +60 -0
- package/dist/runners/options.js +56 -0
- package/dist/runners/qemu-image.js +183 -0
- package/dist/runners/rules.js +49 -0
- package/dist/runners/ubuntu-bridges.js +154 -0
- package/dist/runners/ubuntu-guest.js +136 -0
- package/dist/runners/ubuntu-image.js +26 -0
- package/dist/runners/ubuntu-rules.js +86 -0
- package/dist/runners/ubuntu-shared.js +65 -0
- package/dist/runners/ubuntu-summary.js +141 -0
- package/dist/runners/ubuntu.js +117 -0
- package/dist/runners/vmware-common.js +158 -0
- package/dist/runners/vmware-image.js +242 -0
- package/dist/runners/windows-autologon.js +119 -0
- package/dist/runners/windows-bridges.js +136 -0
- package/dist/runners/windows-guest.js +213 -0
- package/dist/runners/windows-image.js +23 -0
- package/dist/runners/windows-qemu-summary.js +85 -0
- package/dist/runners/windows-qemu.js +189 -0
- package/dist/runners/windows-shared.js +68 -0
- package/dist/runners/windows-summary.js +104 -0
- package/dist/runners/windows.js +96 -0
- package/dist/settings/common.js +107 -0
- package/dist/settings/macos-copy.js +223 -0
- package/dist/settings/macos.js +41 -0
- package/dist/settings/ubuntu-copy.js +223 -0
- package/dist/settings/ubuntu.js +68 -0
- package/package.json +52 -0
|
@@ -0,0 +1,1036 @@
|
|
|
1
|
+
packer {
|
|
2
|
+
required_version = ">= 1.10.0"
|
|
3
|
+
|
|
4
|
+
required_plugins {
|
|
5
|
+
vmware = {
|
|
6
|
+
version = ">= 2.1.5"
|
|
7
|
+
source = "github.com/vmware/vmware"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
# ---------------------------------------------------------------------------
|
|
13
|
+
# Variables
|
|
14
|
+
# ---------------------------------------------------------------------------
|
|
15
|
+
#
|
|
16
|
+
# The Windows build is shared between the QEMU and VMware platforms: the
|
|
17
|
+
# toolchain, credentials and OpenChamber vars mirror
|
|
18
|
+
# images/windows-arm64-qemu/sandbox.pkr.hcl. The VMware build differs in the
|
|
19
|
+
# mechanics — VMware Fusion hosts the installer and the VM is exported as a
|
|
20
|
+
# .vmx + .vmdk instead of a qcow2.
|
|
21
|
+
|
|
22
|
+
variable "windows_version" {
|
|
23
|
+
type = string
|
|
24
|
+
description = "Windows guest version, e.g. '11'; part of the image name (sandbox-windows-<windows_version>-arm64-vmware)."
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
variable "image_version" {
|
|
28
|
+
type = string
|
|
29
|
+
description = "Semantic version this image is published under; bump it + add a CHANGELOG.md entry per release."
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# --- Build layout ---
|
|
33
|
+
#
|
|
34
|
+
# Built images and their scratch land in a top-level build/ directory:
|
|
35
|
+
# build/windows-arm64-vmware/output (Packer's output_directory) and
|
|
36
|
+
# build/windows-arm64-vmware/drivers/ (staged into the unattend CD).
|
|
37
|
+
# The agent-dev-env CLI build flow computes the directory and passes
|
|
38
|
+
# it in; the default keeps a bare `packer build` from the platform dir
|
|
39
|
+
# working.
|
|
40
|
+
|
|
41
|
+
variable "build_dir" {
|
|
42
|
+
type = string
|
|
43
|
+
default = "."
|
|
44
|
+
description = "Per-image build directory: <build_dir>/output holds the built image and <build_dir>/drivers the staged unattend-CD drivers. Set by `agent-dev-env build` to build/windows-arm64-vmware/."
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# --- Windows install ISO (bring-your-own, see images/windows-arm64-vmware/README.md) ---
|
|
48
|
+
|
|
49
|
+
variable "iso_path" {
|
|
50
|
+
type = string
|
|
51
|
+
description = "Absolute path to the Windows 11 ARM64 ISO. Set by the agent-dev-env CLI build flow (PKR_VAR_iso_path); the ISO is not redistributable so it cannot live in the repo. Required — validate with `-var iso_path=/path/to/iso`."
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
variable "iso_sha256" {
|
|
55
|
+
type = string
|
|
56
|
+
default = ""
|
|
57
|
+
description = "SHA256 of the Windows ISO as published on the Microsoft download page. Read by the agent-dev-env CLI build flow for verification, not by Packer itself (iso_checksum is 'none')."
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# --- VMware Fusion (host hypervisor + ARM64 boot drivers + tools ISO) ---
|
|
61
|
+
|
|
62
|
+
variable "vmware_fusion_app_path" {
|
|
63
|
+
type = string
|
|
64
|
+
default = "/Applications/VMware Fusion.app"
|
|
65
|
+
description = "Path of the VMware Fusion installation. Packer's vmware-iso builder drives Fusion via vmrun; the ARM64 Windows boot drivers (drivers-arm64.zip) and the ARM64 VMware Tools ISO (isoimages/arm64/windows.iso) come from the app bundle."
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# --- Toolchain versions (installed via Chocolatey) ---
|
|
69
|
+
|
|
70
|
+
variable "nodejs_version" {
|
|
71
|
+
type = string
|
|
72
|
+
description = "Node.js version, e.g. '26.8.1' (choco package version)."
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
variable "python_version" {
|
|
76
|
+
type = string
|
|
77
|
+
description = "Python version, e.g. '3.13.1' (choco package version)."
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
variable "github_cli_version" {
|
|
81
|
+
type = string
|
|
82
|
+
description = "GitHub CLI version, e.g. '2.97.0' (choco package version)."
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
variable "ripgrep_version" {
|
|
86
|
+
type = string
|
|
87
|
+
description = "ripgrep version, e.g. '15.2.0' (choco package version)."
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
variable "git_version" {
|
|
91
|
+
type = string
|
|
92
|
+
description = "Git version, e.g. '2.55.0.4' (choco package version)."
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
variable "jq_version" {
|
|
96
|
+
type = string
|
|
97
|
+
description = "jq version, e.g. '1.8.1' (choco package version)."
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
variable "open_code_review_version" {
|
|
101
|
+
type = string
|
|
102
|
+
description = "open-code-review (ocr) version installed via npm."
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
variable "chrome_version" {
|
|
106
|
+
type = string
|
|
107
|
+
description = "Google Chrome version (Chrome for Testing snapshot, e.g. '152.0.7977.54')."
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
variable "chrome_sha256" {
|
|
111
|
+
type = string
|
|
112
|
+
description = "SHA256 of the CfT chrome-win64.zip for chrome_version."
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# --- C/C++ + cross-language toolchains (brought over from AdGuard's
|
|
116
|
+
# build-agent-images windows2022-vs2022 image) ---
|
|
117
|
+
|
|
118
|
+
variable "vs_buildtools_version" {
|
|
119
|
+
type = string
|
|
120
|
+
description = "VS2022 Build Tools choco package version, e.g. '117.14.37' (the installer is then finalized with setup.exe + the .NET SDK / VC++ / Win11 SDK components)."
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
variable "go_version" {
|
|
124
|
+
type = string
|
|
125
|
+
description = "Go version (choco package version)."
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
variable "rust_version" {
|
|
129
|
+
type = string
|
|
130
|
+
description = "Rust toolchain version installed via rustup (e.g. '1.95'; rustup resolves the latest patch)."
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
variable "wixtoolset_version" {
|
|
134
|
+
type = string
|
|
135
|
+
description = "WiX Toolset version (choco package version)."
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
variable "protoc_version" {
|
|
139
|
+
type = string
|
|
140
|
+
description = "protobuf compiler (protoc) version (choco package version)."
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
variable "nasm_version" {
|
|
144
|
+
type = string
|
|
145
|
+
description = "NASM assembler version (choco package version)."
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
variable "llvm_version" {
|
|
149
|
+
type = string
|
|
150
|
+
description = "LLVM version (choco package version)."
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
variable "vim_version" {
|
|
154
|
+
type = string
|
|
155
|
+
description = "Vim version (choco package version)."
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
variable "nuget_version" {
|
|
159
|
+
type = string
|
|
160
|
+
description = "NuGet command-line client version (choco package version)."
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
variable "mingw_version" {
|
|
164
|
+
type = string
|
|
165
|
+
description = "MinGW-w64 GCC toolchain version (choco package version; provides make + mingw gcc)."
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
variable "make_version" {
|
|
169
|
+
type = string
|
|
170
|
+
description = "GNU make version (choco package version)."
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# --- VM resources ---
|
|
174
|
+
|
|
175
|
+
variable "disk_size" {
|
|
176
|
+
type = number
|
|
177
|
+
default = 100
|
|
178
|
+
description = "VM disk size in GB."
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
variable "cpu_count" {
|
|
182
|
+
type = number
|
|
183
|
+
default = 4
|
|
184
|
+
description = "CPU count of the VM."
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
variable "memory_gb" {
|
|
188
|
+
type = number
|
|
189
|
+
default = 8
|
|
190
|
+
description = "RAM of the VM in GB."
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
# --- WinRM credentials (baked into autounattend.xml — keep in sync) ---
|
|
194
|
+
|
|
195
|
+
variable "winrm_username" {
|
|
196
|
+
type = string
|
|
197
|
+
default = "Administrator"
|
|
198
|
+
description = "User used for WinRM provisioning; must match the account autounattend.xml creates."
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
variable "winrm_password" {
|
|
202
|
+
type = string
|
|
203
|
+
default = "sandbox1"
|
|
204
|
+
sensitive = true
|
|
205
|
+
description = "Password of the provisioning account; must match autounattend.xml."
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# --- OpenChamber web UI ---
|
|
209
|
+
|
|
210
|
+
variable "openchamber_ui_password" {
|
|
211
|
+
type = string
|
|
212
|
+
default = "sandbox"
|
|
213
|
+
description = "Password protecting the OpenChamber web UI; required because the server binds to 0.0.0.0 (host access: http://<guest-ip>:4000)."
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
variable "openchamber_port" {
|
|
217
|
+
type = number
|
|
218
|
+
default = 4000
|
|
219
|
+
description = "TCP port the OpenChamber web UI listens on inside the guest."
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# ---------------------------------------------------------------------------
|
|
223
|
+
# Source: VMware (Fusion on Apple Silicon, ARM64 guest)
|
|
224
|
+
# ---------------------------------------------------------------------------
|
|
225
|
+
#
|
|
226
|
+
# Windows 11 ARM64 runs natively on VMware Fusion on Apple Silicon — the
|
|
227
|
+
# most proven Windows-ARM path (Fusion ships the ARM64 guest drivers and
|
|
228
|
+
# tools). The recipe mirrors the reference implementation in
|
|
229
|
+
# gusztavvargadr/packer (src/windows/source.vmware.pkr.hcl +
|
|
230
|
+
# samples/windows-11/images.pkrvars.hcl, Unlicense), adapted to this repo's
|
|
231
|
+
# conventions:
|
|
232
|
+
# - guest_os_type "arm-windows11-64", hardware version 20, NVMe disk,
|
|
233
|
+
# vmxnet3 NIC under NAT, EFI firmware — the proven ARM64 combo,
|
|
234
|
+
# - the unattend CD also carries the vmxnet3 ARM64 network driver
|
|
235
|
+
# (drivers/staging/, staged by the build flow from Fusion's
|
|
236
|
+
# Contents/Library/isoimages/arm64/drivers-arm64.zip). Without it there
|
|
237
|
+
# is NO NIC in the guest and WinRM is unreachable from the host — the
|
|
238
|
+
# driver must land before any network use (FirstLogonCommands order 1),
|
|
239
|
+
# - VMware Tools ARM64 (the ISO Fusion bundles at
|
|
240
|
+
# Contents/Library/isoimages/arm64/windows.iso) is attached by the
|
|
241
|
+
# builder (tools_mode "attach"); autounattend.xml installs it at
|
|
242
|
+
# first logon, before WinRM — the installer rebinds the NIC and
|
|
243
|
+
# would kill a live WinRM session (observed as"dial tcp ... wsman:
|
|
244
|
+
# operation timed out"). Tools
|
|
245
|
+
# are what make vmrun's getGuestIPAddress work in the sandbox runner,
|
|
246
|
+
# - NVMe storage needs no extra driver: Windows 11 ARM64 has the NVMe
|
|
247
|
+
# driver in-box (unlike QEMU's virtio disk, which needs viostor in
|
|
248
|
+
# WinPE).
|
|
249
|
+
#
|
|
250
|
+
# No snapshot is created (snapshot_name): the sandbox runner makes a full
|
|
251
|
+
# vmrun clone as its working VM, and a snapshot inside the published image
|
|
252
|
+
# would only complicate disk compaction and re-clones.
|
|
253
|
+
|
|
254
|
+
source "vmware-iso" "windows" {
|
|
255
|
+
vm_name = "sandbox-windows-${var.windows_version}-arm64-vmware"
|
|
256
|
+
output_directory = "${var.build_dir}/output"
|
|
257
|
+
|
|
258
|
+
# The build flow (agent-dev-env build) verifies the ISO
|
|
259
|
+
# against var.iso_sha256 before Packer runs; iso_checksum is "none" so
|
|
260
|
+
# Packer does not re-verify.
|
|
261
|
+
iso_url = var.iso_path
|
|
262
|
+
iso_checksum = "none"
|
|
263
|
+
|
|
264
|
+
cpus = var.cpu_count
|
|
265
|
+
memory = var.memory_gb * 1024
|
|
266
|
+
disk_size = var.disk_size * 1024
|
|
267
|
+
|
|
268
|
+
# --- ARM64 guest wiring (proven in gusztavvargadr/packer) ---
|
|
269
|
+
version = 20
|
|
270
|
+
guest_os_type = "arm-windows11-64"
|
|
271
|
+
disk_type_id = "0"
|
|
272
|
+
disk_adapter_type = "nvme"
|
|
273
|
+
network = "nat"
|
|
274
|
+
network_adapter_type = "vmxnet3"
|
|
275
|
+
firmware = "efi"
|
|
276
|
+
cdrom_adapter_type = "sata"
|
|
277
|
+
|
|
278
|
+
# Unattend CD: Setup probes attached media for Autounattend.xml at the
|
|
279
|
+
# root, so no keyboard input is needed for the answer file. The same CD
|
|
280
|
+
# carries the staged vmxnet3 ARM64 driver flat in the root (see the
|
|
281
|
+
# FirstLogonCommands driver scan in autounattend.xml). Note: glob the
|
|
282
|
+
# staged files rather than passing the directory — the plugin copies
|
|
283
|
+
# directory cd_files into a temp tree where nested paths break.
|
|
284
|
+
cd_files = ["./autounattend.xml", "${var.build_dir}/drivers/staging/*"]
|
|
285
|
+
cd_label = "UNATTEND"
|
|
286
|
+
|
|
287
|
+
# VMware Tools ARM64 from the Fusion install (matching the host's Fusion
|
|
288
|
+
# version — no download, no version pinning). Attached as a CD-ROM for
|
|
289
|
+
# the whole build and removed on completion; autounattend.xml's
|
|
290
|
+
# FirstLogonCommands installs it before WinRM comes up (the installer
|
|
291
|
+
# rebinds the NIC and would kill a live WinRM session). Requires
|
|
292
|
+
# Fusion 13.6+ (the plugin's minimum) and makes vmrun guest tooling
|
|
293
|
+
# (getGuestIPAddress, shared folders) available at runtime.
|
|
294
|
+
tools_mode = "attach"
|
|
295
|
+
tools_source_path = "${var.vmware_fusion_app_path}/Contents/Library/isoimages/arm64/windows.iso"
|
|
296
|
+
|
|
297
|
+
vmx_data = {
|
|
298
|
+
# The unattend/tools CD-ROMs ride on the SATA controller.
|
|
299
|
+
"sata1.present" = "TRUE"
|
|
300
|
+
# USB 3.x controller — Windows 11 ARM64 has in-box xHCI drivers; the
|
|
301
|
+
# plugin auto-enables USB on Apple Silicon for its own VNC typer, and
|
|
302
|
+
# keeping it present makes the USB input devices work in the guest.
|
|
303
|
+
"usb_xhci.present" = "TRUE"
|
|
304
|
+
# Boot the install CD before the (empty) NVMe disk, so the firmware
|
|
305
|
+
# reaches the ISO's "Press any key to boot from CD" prompt as early
|
|
306
|
+
# as possible.
|
|
307
|
+
"bios.bootorder" = "cdrom,hdd"
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# WinRM, not SSH: it comes up via autounattend's FirstLogonCommands
|
|
311
|
+
# before any OpenSSH server exists, and DISM online servicing needs the
|
|
312
|
+
# elevated token the provisioners request.
|
|
313
|
+
communicator = "winrm"
|
|
314
|
+
winrm_username = var.winrm_username
|
|
315
|
+
winrm_password = var.winrm_password
|
|
316
|
+
winrm_timeout = "90m"
|
|
317
|
+
winrm_port = 5985
|
|
318
|
+
|
|
319
|
+
# Headless: no Fusion window, and the VNC server stays available for the
|
|
320
|
+
# build watchdog (bundled watch-build.py) — pinned so the build flow
|
|
321
|
+
# can start it before `packer build`. The vmware plugin's VNC
|
|
322
|
+
# server is bound to 127.0.0.1; the password is disabled so the watchdog
|
|
323
|
+
# (which assumes an unauthenticated VNC, like the QEMU plugin's) can
|
|
324
|
+
# connect.
|
|
325
|
+
headless = true
|
|
326
|
+
|
|
327
|
+
vnc_bind_address = "127.0.0.1"
|
|
328
|
+
vnc_port_min = 5901
|
|
329
|
+
vnc_port_max = 5901
|
|
330
|
+
vnc_disable_password = true
|
|
331
|
+
|
|
332
|
+
# The ISO's EFI "Press any key to boot from CD or DVD" prompt appears a
|
|
333
|
+
# few seconds after power-on; Enter presses spread over ~25 s cover
|
|
334
|
+
# early and late appearances without spamming keys into Setup like the
|
|
335
|
+
# QEMU template does (no stray keys, no Cancel-dialog watch needed).
|
|
336
|
+
# The build watchdog (bundled watch-build.py) also answers the prompt
|
|
337
|
+
# and rescues a boot that lands in the EFI shell.
|
|
338
|
+
boot_wait = "1s"
|
|
339
|
+
boot_command = [
|
|
340
|
+
"<enter><wait5><enter><wait5><enter><wait5><enter><wait5><enter><wait5><enter>",
|
|
341
|
+
]
|
|
342
|
+
|
|
343
|
+
shutdown_command = "shutdown /s /t 10"
|
|
344
|
+
shutdown_timeout = "10m"
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
# ---------------------------------------------------------------------------
|
|
348
|
+
# Build
|
|
349
|
+
# ---------------------------------------------------------------------------
|
|
350
|
+
|
|
351
|
+
build {
|
|
352
|
+
sources = ["source.vmware-iso.windows"]
|
|
353
|
+
|
|
354
|
+
# ===== Preamble: settle after OOBE, dump versions =====
|
|
355
|
+
provisioner "powershell" {
|
|
356
|
+
elevated_user = var.winrm_username
|
|
357
|
+
elevated_password = var.winrm_password
|
|
358
|
+
inline = [
|
|
359
|
+
"$ErrorActionPreference = 'Stop'",
|
|
360
|
+
"$ProgressPreference = 'SilentlyContinue'",
|
|
361
|
+
"$os = Get-CimInstance Win32_OperatingSystem",
|
|
362
|
+
"Write-Host \"Windows install complete: $($os.Caption) build $($os.BuildNumber) ($env:PROCESSOR_ARCHITECTURE)\"",
|
|
363
|
+
"Write-Host 'Waiting 30s for post-OOBE first-logon scripts to settle...'",
|
|
364
|
+
"Start-Sleep -Seconds 30",
|
|
365
|
+
]
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
# ===== VMware Tools =====
|
|
369
|
+
# VMware Tools are installed by autounattend.xml's FirstLogonCommands
|
|
370
|
+
# (order 2, BEFORE WinRM comes up) — the tools installer rebinds the
|
|
371
|
+
# NIC and drops every live WinRM session mid-install, so a provisioner
|
|
372
|
+
# here would kill its own connection (observed as a hard "dial tcp ...
|
|
373
|
+
# wsman: operation timed out" failure). The tools are what make vmrun
|
|
374
|
+
# getGuestIPAddress and HGFS shared folders work in the sandbox runner;
|
|
375
|
+
# the build fails softly if the CD was not found.
|
|
376
|
+
|
|
377
|
+
# ===== Chocolatey + .NET strong crypto =====
|
|
378
|
+
provisioner "powershell" {
|
|
379
|
+
elevated_user = var.winrm_username
|
|
380
|
+
elevated_password = var.winrm_password
|
|
381
|
+
inline = [
|
|
382
|
+
"$ErrorActionPreference = 'Stop'",
|
|
383
|
+
"[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072",
|
|
384
|
+
"iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))",
|
|
385
|
+
"if ($LASTEXITCODE -ne 0) { throw \"chocolatey install failed: $LASTEXITCODE\" }",
|
|
386
|
+
"$chocoInstall = [System.Environment]::GetEnvironmentVariable('ChocolateyInstall','Machine')",
|
|
387
|
+
"if (-not $chocoInstall) { $chocoInstall = 'C:\\ProgramData\\chocolatey' }",
|
|
388
|
+
"$chocoBin = Join-Path $chocoInstall 'bin'",
|
|
389
|
+
"# Persist the Chocolatey bin dir into the machine PATH ourselves: the",
|
|
390
|
+
"# bootstrapper's compiled Install-ChocolateyPath cmdlet asserts UAC",
|
|
391
|
+
"# before writing and can fail to persist the registry value in the",
|
|
392
|
+
"# elevated WinRM context (observed as 'choco' not recognized in the",
|
|
393
|
+
"# toolchain phase after the reboot, even with PATH re-read).",
|
|
394
|
+
"$machinePath = [System.Environment]::GetEnvironmentVariable('Path','Machine').TrimEnd(';')",
|
|
395
|
+
"if (-not (($machinePath -split ';') -contains $chocoBin)) {",
|
|
396
|
+
" [System.Environment]::SetEnvironmentVariable('Path', $machinePath + ';' + $chocoBin, 'Machine')",
|
|
397
|
+
"}",
|
|
398
|
+
"# Run choco by its full path too — this session's PATH is not",
|
|
399
|
+
"# guaranteed to contain the bin dir above.",
|
|
400
|
+
"$choco = Join-Path $chocoBin 'choco.exe'",
|
|
401
|
+
"if (-not (Test-Path $choco)) { throw \"choco.exe not found at $choco\" }",
|
|
402
|
+
"& $choco feature enable -n allowGlobalConfirmation",
|
|
403
|
+
"if ($LASTEXITCODE -ne 0) { throw \"choco feature enable failed: $LASTEXITCODE\" }",
|
|
404
|
+
"# Legacy .NET strong-crypto: without it, older TLS stacks fail against modern hosts",
|
|
405
|
+
"reg add 'HKLM\\SOFTWARE\\Microsoft\\.NETFramework\\v4.0.30319' /v SchUseStrongCrypto /t REG_DWORD /d 1 /f",
|
|
406
|
+
"reg add 'HKLM\\SOFTWARE\\Wow6432Node\\Microsoft\\.NETFramework\\v4.0.30319' /v SchUseStrongCrypto /t REG_DWORD /d 1 /f",
|
|
407
|
+
]
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
# ===== Reboot: clear the tools-install pending reboot =====
|
|
411
|
+
# The VMware Tools install (autounattend FirstLogonCommands) leaves a
|
|
412
|
+
# pending reboot, which makes `choco install` return 3010 (seen on
|
|
413
|
+
# python) and makes the .NET Framework 4.8 Developer Pack installer
|
|
414
|
+
# fail with exit code 1 (it refuses to run while a reboot is pending).
|
|
415
|
+
# Restart once and wait for WinRM, so the toolchain + VS phases run on
|
|
416
|
+
# a cleanly booted machine.
|
|
417
|
+
provisioner "windows-restart" {
|
|
418
|
+
restart_timeout = "30m"
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
# ===== Toolchain via Chocolatey (versions from the vars file) =====
|
|
422
|
+
provisioner "powershell" {
|
|
423
|
+
elevated_user = var.winrm_username
|
|
424
|
+
elevated_password = var.winrm_password
|
|
425
|
+
inline = [<<-END
|
|
426
|
+
$ErrorActionPreference = 'Stop'
|
|
427
|
+
$ProgressPreference = 'SilentlyContinue'
|
|
428
|
+
# Choco is called by its full path, never via PATH: the bootstrapper
|
|
429
|
+
# can fail to persist the machine PATH (see the Chocolatey
|
|
430
|
+
# provisioner), and after the tools reboot a fresh WinRM process can
|
|
431
|
+
# inherit a stale PATH — both observed as 'choco' not recognized.
|
|
432
|
+
# The PATH is still re-read from the registry below for the tools
|
|
433
|
+
# this phase installs.
|
|
434
|
+
$chocoInstall = [System.Environment]::GetEnvironmentVariable('ChocolateyInstall','Machine')
|
|
435
|
+
if (-not $chocoInstall) { $chocoInstall = 'C:\ProgramData\chocolatey' }
|
|
436
|
+
$choco = Join-Path (Join-Path $chocoInstall 'bin') 'choco.exe'
|
|
437
|
+
if (-not (Test-Path $choco)) { throw "choco.exe not found at $choco (Chocolatey install failed?)" }
|
|
438
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path','User')
|
|
439
|
+
# choco downloads can 404 transiently over the VM's NAT (observed
|
|
440
|
+
# with the pinned python package); retry each install before giving
|
|
441
|
+
# up. The command goes through cmd /c so the package name and its
|
|
442
|
+
# --version are parsed as separate arguments (passing $installArgs
|
|
443
|
+
# straight to the exe makes choco look for a package literally named
|
|
444
|
+
# 'nodejs --version=26.8.1'). No 2>&1 here: redirecting native
|
|
445
|
+
# stderr under $ErrorActionPreference='Stop' turns any choco warning
|
|
446
|
+
# into a terminating error in Windows PowerShell 5.1 (the repo's
|
|
447
|
+
# documented native-stderr bug) — the output just streams to the
|
|
448
|
+
# session log and $LASTEXITCODE decides the retry.
|
|
449
|
+
function Invoke-ChocoRetry([string]$installArgs) {
|
|
450
|
+
for ($try = 1; $try -le 3; $try++) {
|
|
451
|
+
cmd /c "$choco install $installArgs -y"
|
|
452
|
+
if ($LASTEXITCODE -eq 0) { return }
|
|
453
|
+
Write-Host "choco install $installArgs attempt $try failed ($LASTEXITCODE); retrying"
|
|
454
|
+
Start-Sleep -Seconds 15
|
|
455
|
+
}
|
|
456
|
+
throw "choco install $installArgs failed after 3 attempts"
|
|
457
|
+
}
|
|
458
|
+
Invoke-ChocoRetry "nodejs --version=${var.nodejs_version}"
|
|
459
|
+
Invoke-ChocoRetry "gh --version=${var.github_cli_version}"
|
|
460
|
+
Invoke-ChocoRetry "ripgrep --version=${var.ripgrep_version}"
|
|
461
|
+
Invoke-ChocoRetry "git --version=${var.git_version}"
|
|
462
|
+
Invoke-ChocoRetry "jq --version=${var.jq_version}"
|
|
463
|
+
Invoke-ChocoRetry "python --version=${var.python_version}"
|
|
464
|
+
Invoke-ChocoRetry "firefox"
|
|
465
|
+
Invoke-ChocoRetry "curl"
|
|
466
|
+
Invoke-ChocoRetry "docker-cli"
|
|
467
|
+
Invoke-ChocoRetry "docker-compose"
|
|
468
|
+
# C/C++ + cross-language toolchains (brought over from AdGuard's
|
|
469
|
+
# build-agent-images windows2022-vs2022 / windows2022-go images).
|
|
470
|
+
# VS2022 Build Tools (with its .NET/VC++ workloads) and Rust are
|
|
471
|
+
# installed by their own provisioners below.
|
|
472
|
+
Invoke-ChocoRetry "golang --version=${var.go_version}"
|
|
473
|
+
Invoke-ChocoRetry "mingw --version=${var.mingw_version}"
|
|
474
|
+
Invoke-ChocoRetry "make --version=${var.make_version}"
|
|
475
|
+
Invoke-ChocoRetry "vim --version=${var.vim_version}"
|
|
476
|
+
Invoke-ChocoRetry "nuget.commandline --version=${var.nuget_version}"
|
|
477
|
+
Invoke-ChocoRetry "protoc --version=${var.protoc_version}"
|
|
478
|
+
Invoke-ChocoRetry "nasm --version=${var.nasm_version}"
|
|
479
|
+
Invoke-ChocoRetry "llvm --version=${var.llvm_version}"
|
|
480
|
+
Invoke-ChocoRetry "wixtoolset --version=${var.wixtoolset_version}"
|
|
481
|
+
|
|
482
|
+
# ===== Google Chrome (CfT snapshot, hash-pinned) =====
|
|
483
|
+
# choco's googlechrome package downloads the live dl.google.com MSI,
|
|
484
|
+
# whose binary rotates with every Chrome release — the pinned hash
|
|
485
|
+
# breaks between releases (observed with 152.0.7977.54). Instead we
|
|
486
|
+
# fetch the versioned Chrome for Testing archive, verify the SHA256,
|
|
487
|
+
# and extract it into the standard install location.
|
|
488
|
+
$chromeUrl = "https://storage.googleapis.com/chrome-for-testing-public/${var.chrome_version}/win64/chrome-win64.zip"
|
|
489
|
+
$chromeZip = "$env:TEMP\chrome-win64.zip"
|
|
490
|
+
$chromeAppDir = 'C:\Program Files\Google\Chrome\Application'
|
|
491
|
+
$chromeExe = Join-Path $chromeAppDir 'chrome.exe'
|
|
492
|
+
# 202 MB over a VM NAT can drop mid-transfer; retry.
|
|
493
|
+
$downloaded = $false
|
|
494
|
+
for ($try = 1; $try -le 3 -and -not $downloaded; $try++) {
|
|
495
|
+
try {
|
|
496
|
+
Invoke-WebRequest -UseBasicParsing -Uri $chromeUrl -OutFile $chromeZip -ErrorAction Stop
|
|
497
|
+
$downloaded = $true
|
|
498
|
+
} catch {
|
|
499
|
+
Write-Host "chrome download attempt $try failed: $($_.Exception.Message)"
|
|
500
|
+
if ($try -lt 3) { Start-Sleep -Seconds 10 }
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
if (-not $downloaded) { throw "chrome download failed after 3 attempts" }
|
|
504
|
+
$actual = (Get-FileHash -Path $chromeZip -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
505
|
+
if ($actual -ne '${var.chrome_sha256}') {
|
|
506
|
+
throw "chrome checksum mismatch: expected ${var.chrome_sha256}, got $actual"
|
|
507
|
+
}
|
|
508
|
+
New-Item -ItemType Directory -Force -Path "$env:TEMP\chrome-x" | Out-Null
|
|
509
|
+
Expand-Archive -Path $chromeZip -DestinationPath "$env:TEMP\chrome-x" -Force
|
|
510
|
+
New-Item -ItemType Directory -Force -Path $chromeAppDir | Out-Null
|
|
511
|
+
Copy-Item -Path "$env:TEMP\chrome-x\chrome-win64\*" -Destination $chromeAppDir -Recurse -Force
|
|
512
|
+
Remove-Item -Path "$env:TEMP\chrome-x", $chromeZip -Recurse -Force
|
|
513
|
+
# Register App Paths so `start chrome` / shell launchers resolve it
|
|
514
|
+
$appPaths = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe'
|
|
515
|
+
New-Item -Path $appPaths -Force | Out-Null
|
|
516
|
+
New-ItemProperty -Path $appPaths -Name '(Default)' -Value $chromeExe -PropertyType String -Force | Out-Null
|
|
517
|
+
if (-not (Test-Path $chromeExe)) { throw "chrome.exe not found after extraction" }
|
|
518
|
+
$toolPaths = @(
|
|
519
|
+
'C:\Program Files\nodejs',
|
|
520
|
+
'C:\ProgramData\chocolatey\bin',
|
|
521
|
+
'C:\Program Files\Git\cmd',
|
|
522
|
+
'C:\Program Files\Git\bin',
|
|
523
|
+
'C:\Program Files\Google\Chrome\Application',
|
|
524
|
+
'C:\Program Files\Go\bin',
|
|
525
|
+
'C:\Program Files\LLVM\bin',
|
|
526
|
+
'C:\Program Files\NASM',
|
|
527
|
+
# WiX 3.14.x: newer package versions install the MSI under
|
|
528
|
+
# 'Program Files\WiX Toolset v3.14\bin' (older ones put the
|
|
529
|
+
# binaries in the choco lib) — add both, the guards skip absent.
|
|
530
|
+
'C:\Program Files (x86)\WiX Toolset v3.14\bin',
|
|
531
|
+
'C:\ProgramData\chocolatey\lib\wixtoolset\tools',
|
|
532
|
+
'C:\mingw64\bin',
|
|
533
|
+
"$env:APPDATA\npm"
|
|
534
|
+
)
|
|
535
|
+
$machinePath = [System.Environment]::GetEnvironmentVariable('Path','Machine').TrimEnd(';')
|
|
536
|
+
foreach ($toolPath in $toolPaths) {
|
|
537
|
+
if ((Test-Path $toolPath) -and -not (($machinePath -split ';') -contains $toolPath)) {
|
|
538
|
+
$machinePath = $machinePath + ';' + $toolPath
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
[System.Environment]::SetEnvironmentVariable('Path', $machinePath, 'Machine')
|
|
542
|
+
$env:Path = $machinePath + ';' + [System.Environment]::GetEnvironmentVariable('Path','User')
|
|
543
|
+
node --version
|
|
544
|
+
git --version
|
|
545
|
+
gh --version
|
|
546
|
+
rg --version
|
|
547
|
+
jq --version
|
|
548
|
+
python --version
|
|
549
|
+
go version
|
|
550
|
+
END
|
|
551
|
+
]
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
# ===== Visual Studio 2022 Build Tools (MSVC + .NET SDKs + CMake + Win11 SDK) =====
|
|
555
|
+
# Brought over from AdGuard's build-agent-images windows2022-base-vs2022
|
|
556
|
+
# image: the choco package installs the VS bootstrapper, then setup.exe
|
|
557
|
+
# adds the .NET 4.8 + .NET Core SDKs, the VC++ workload (x86/x64/ARM/
|
|
558
|
+
# ARM64 — ARM64 matters for this ARM64 guest) and the Windows 11 SDK.
|
|
559
|
+
# On ARM64 the bootstrapper runs as an x86 binary under emulation, which
|
|
560
|
+
# VS2022 supports.
|
|
561
|
+
provisioner "powershell" {
|
|
562
|
+
elevated_user = var.winrm_username
|
|
563
|
+
elevated_password = var.winrm_password
|
|
564
|
+
timeout = "90m"
|
|
565
|
+
inline = [<<-END
|
|
566
|
+
$ErrorActionPreference = 'Stop'
|
|
567
|
+
$ProgressPreference = 'SilentlyContinue'
|
|
568
|
+
# Choco is called by its full path, never via PATH (see the
|
|
569
|
+
# toolchain provisioner). The PATH is still re-read for the tools
|
|
570
|
+
# this phase installs.
|
|
571
|
+
$chocoInstall = [System.Environment]::GetEnvironmentVariable('ChocolateyInstall','Machine')
|
|
572
|
+
if (-not $chocoInstall) { $chocoInstall = 'C:\ProgramData\chocolatey' }
|
|
573
|
+
$choco = Join-Path (Join-Path $chocoInstall 'bin') 'choco.exe'
|
|
574
|
+
if (-not (Test-Path $choco)) { throw "choco.exe not found at $choco (Chocolatey install failed?)" }
|
|
575
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path','User')
|
|
576
|
+
# Standalone .NET Framework 4.8 Developer Pack (latest), as in the
|
|
577
|
+
# AdGuard image — MSBuild/.NET 4.8 targets need it. 3010 = success,
|
|
578
|
+
# reboot required, which choco also returns for the KB dep.
|
|
579
|
+
& $choco install netfx-4.8-devpack -y --norestart
|
|
580
|
+
if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne 3010) { throw "choco netfx-4.8-devpack failed: $LASTEXITCODE" }
|
|
581
|
+
& $choco install visualstudio2022buildtools --version=${var.vs_buildtools_version} -y --norestart --wait --nocache --noUpdateInstaller
|
|
582
|
+
if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne 3010) { throw "choco visualstudio2022buildtools failed: $LASTEXITCODE" }
|
|
583
|
+
$setup = 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\setup.exe'
|
|
584
|
+
if (-not (Test-Path $setup)) { throw "VS installer not found: $setup" }
|
|
585
|
+
# .NET 4.8 SDK + .NET Core SDK workloads
|
|
586
|
+
& $setup modify --quiet --norestart `
|
|
587
|
+
--installPath 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools' `
|
|
588
|
+
--add Microsoft.Net.Component.4.8.SDK `
|
|
589
|
+
--add Microsoft.NetCore.Component.SDK `
|
|
590
|
+
| Out-Host
|
|
591
|
+
if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne 3010) { throw "vs .NET workloads failed: $LASTEXITCODE" }
|
|
592
|
+
# VC++ workload (x86/x64/ARM/ARM64) + CMake project support
|
|
593
|
+
& $setup modify --quiet --norestart `
|
|
594
|
+
--installPath 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools' `
|
|
595
|
+
--add Microsoft.VisualStudio.Workload.VCTools `
|
|
596
|
+
--add Microsoft.VisualStudio.Component.VC.CMake.Project `
|
|
597
|
+
--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
|
|
598
|
+
--add Microsoft.VisualStudio.Component.VC.Tools.ARM `
|
|
599
|
+
--add Microsoft.VisualStudio.Component.VC.Tools.ARM64 `
|
|
600
|
+
| Out-Host
|
|
601
|
+
if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne 3010) { throw "vs VC++ workloads failed: $LASTEXITCODE" }
|
|
602
|
+
# Windows 11 SDK (the components the Win11 toolchain expects)
|
|
603
|
+
& $setup modify --quiet --norestart `
|
|
604
|
+
--installPath 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools' `
|
|
605
|
+
--add Microsoft.VisualStudio.Component.Windows11SDK.22621 `
|
|
606
|
+
| Out-Host
|
|
607
|
+
if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne 3010) { throw "vs Windows11SDK failed: $LASTEXITCODE" }
|
|
608
|
+
$vcPath = 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build'
|
|
609
|
+
$machinePath = [System.Environment]::GetEnvironmentVariable('Path','Machine').TrimEnd(';')
|
|
610
|
+
if (-not (($machinePath -split ';') -contains $vcPath)) {
|
|
611
|
+
[System.Environment]::SetEnvironmentVariable('Path', $machinePath + ';' + $vcPath, 'Machine')
|
|
612
|
+
}
|
|
613
|
+
Write-Host 'Visual Studio 2022 Build Tools + .NET SDKs installed'
|
|
614
|
+
END
|
|
615
|
+
]
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
# ===== Rust via rustup (ARM64 host toolchain + MSVC cross targets) =====
|
|
619
|
+
# Brought over from AdGuard's build-agent-images windows2022-vs2022
|
|
620
|
+
# image, adapted for ARM64: win.rustup.rs/aarch64 ships the aarch64
|
|
621
|
+
# rustup-init (win.rustup.rs/x86_64 is the x86_64 host only). The
|
|
622
|
+
# default-toolchain is resolved by rustup (e.g. '1.95' -> 1.95.x), and
|
|
623
|
+
# the MSVC targets cover x86_64/i686/aarch64 linking.
|
|
624
|
+
provisioner "powershell" {
|
|
625
|
+
elevated_user = var.winrm_username
|
|
626
|
+
elevated_password = var.winrm_password
|
|
627
|
+
timeout = "30m"
|
|
628
|
+
inline = [<<-END
|
|
629
|
+
$ErrorActionPreference = 'Stop'
|
|
630
|
+
$ProgressPreference = 'SilentlyContinue'
|
|
631
|
+
$rustupInit = "$env:TEMP\rustup-init.exe"
|
|
632
|
+
$downloaded = $false
|
|
633
|
+
for ($try = 1; $try -le 3 -and -not $downloaded; $try++) {
|
|
634
|
+
try {
|
|
635
|
+
Invoke-WebRequest -UseBasicParsing -Uri 'https://win.rustup.rs/aarch64' -OutFile $rustupInit -ErrorAction Stop
|
|
636
|
+
$downloaded = $true
|
|
637
|
+
} catch {
|
|
638
|
+
Write-Host "rustup-init download attempt $try failed: $($_.Exception.Message)"
|
|
639
|
+
if ($try -lt 3) { Start-Sleep -Seconds 10 }
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
if (-not $downloaded) { throw "rustup-init download failed after 3 attempts" }
|
|
643
|
+
& $rustupInit -y --default-toolchain ${var.rust_version}
|
|
644
|
+
if ($LASTEXITCODE -ne 0) { throw "rustup-init failed: $LASTEXITCODE" }
|
|
645
|
+
Remove-Item $rustupInit -Force
|
|
646
|
+
$cargoBin = "$env:USERPROFILE\.cargo\bin"
|
|
647
|
+
$machinePath = [System.Environment]::GetEnvironmentVariable('Path','Machine').TrimEnd(';')
|
|
648
|
+
if (-not (($machinePath -split ';') -contains $cargoBin)) {
|
|
649
|
+
[System.Environment]::SetEnvironmentVariable('Path', $machinePath + ';' + $cargoBin, 'Machine')
|
|
650
|
+
}
|
|
651
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path','User')
|
|
652
|
+
rustup target add x86_64-pc-windows-msvc i686-pc-windows-msvc aarch64-pc-windows-msvc
|
|
653
|
+
if ($LASTEXITCODE -ne 0) { throw "rustup target add failed: $LASTEXITCODE" }
|
|
654
|
+
Write-Host "Rust ${var.rust_version} installed with MSVC targets"
|
|
655
|
+
END
|
|
656
|
+
]
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
# ===== Visual Studio Code (native arm64, direct download) =====
|
|
660
|
+
provisioner "powershell" {
|
|
661
|
+
elevated_user = var.winrm_username
|
|
662
|
+
elevated_password = var.winrm_password
|
|
663
|
+
inline = [<<-END
|
|
664
|
+
$ErrorActionPreference = 'Stop'
|
|
665
|
+
$ProgressPreference = 'SilentlyContinue'
|
|
666
|
+
$installer = "$env:TEMP\vscode-arm64-user-setup.exe"
|
|
667
|
+
$downloaded = $false
|
|
668
|
+
for ($try = 1; $try -le 3 -and -not $downloaded; $try++) {
|
|
669
|
+
try {
|
|
670
|
+
Invoke-WebRequest -UseBasicParsing -Uri 'https://update.code.visualstudio.com/latest/win32-arm64-user/stable' -OutFile $installer -ErrorAction Stop
|
|
671
|
+
$downloaded = $true
|
|
672
|
+
} catch {
|
|
673
|
+
Write-Host "vscode download attempt $try failed: $($_.Exception.Message)"
|
|
674
|
+
if ($try -lt 3) { Start-Sleep -Seconds 10 }
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (-not $downloaded) { throw "vscode download failed after 3 attempts" }
|
|
678
|
+
Start-Process $installer -ArgumentList '/VERYSILENT /NORESTART /MERGETASKS=!runcode' -Wait
|
|
679
|
+
Remove-Item $installer -Force
|
|
680
|
+
$codeBin = "$env:LOCALAPPDATA\Programs\Microsoft VS Code\bin"
|
|
681
|
+
if (Test-Path $codeBin) {
|
|
682
|
+
$machinePath = [System.Environment]::GetEnvironmentVariable('Path','Machine').TrimEnd(';')
|
|
683
|
+
if (-not (($machinePath -split ';') -contains $codeBin)) {
|
|
684
|
+
[System.Environment]::SetEnvironmentVariable('Path', $machinePath + ';' + $codeBin, 'Machine')
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
END
|
|
688
|
+
]
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
# ===== OpenCode, OpenCodeReview, OpenChamber web UI =====
|
|
692
|
+
provisioner "powershell" {
|
|
693
|
+
elevated_user = var.winrm_username
|
|
694
|
+
elevated_password = var.winrm_password
|
|
695
|
+
inline = [<<-END
|
|
696
|
+
# EAP=Continue, not Stop: this script runs many native commands
|
|
697
|
+
# (schtasks, npm, openchamber) whose stderr lines become terminating
|
|
698
|
+
# errors under Stop in Windows PowerShell 5.1 (the 2>$null / 2>&1
|
|
699
|
+
# native-stderr bug). All failure handling is explicit below.
|
|
700
|
+
$ErrorActionPreference = 'Continue'
|
|
701
|
+
$ProgressPreference = 'SilentlyContinue'
|
|
702
|
+
# The image ships with Windows' default Restricted execution policy —
|
|
703
|
+
# every build-time powershell invocation passes -ExecutionPolicy
|
|
704
|
+
# Bypass, so nothing is persisted. But opencode/ocr are npm shims
|
|
705
|
+
# (opencode.ps1 in %APPDATA%\npm): a Restricted shell refuses to run
|
|
706
|
+
# them ("running scripts is disabled on this system"). Bake in
|
|
707
|
+
# RemoteSigned machine-wide (this provisioner runs elevated, so the
|
|
708
|
+
# LocalMachine scope persists in the image) and keep the runners'
|
|
709
|
+
# runtime set as a fallback for images built before this change.
|
|
710
|
+
# The build passes -ExecutionPolicy Bypass at Process scope, so
|
|
711
|
+
# setting only LocalMachine triggers Set-ExecutionPolicy's "overridden
|
|
712
|
+
# by a more specific scope" notice, which Windows PowerShell 5.1 under
|
|
713
|
+
# WinRM surfaces as a terminating error that aborts the build even
|
|
714
|
+
# though the machine policy was updated fine (observed). Set the
|
|
715
|
+
# Process scope first (no override, no notice), then the machine.
|
|
716
|
+
try {
|
|
717
|
+
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force -ErrorAction SilentlyContinue
|
|
718
|
+
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine -Force -ErrorAction SilentlyContinue
|
|
719
|
+
} catch {
|
|
720
|
+
Write-Warning "Set-ExecutionPolicy failed: $($_.Exception.Message)"
|
|
721
|
+
}
|
|
722
|
+
Write-Host "PowerShell execution policy: $(Get-ExecutionPolicy -Scope LocalMachine)"
|
|
723
|
+
$machinePath = [System.Environment]::GetEnvironmentVariable('Path','Machine').TrimEnd(';')
|
|
724
|
+
$env:Path = $machinePath + ';' + [System.Environment]::GetEnvironmentVariable('Path','User')
|
|
725
|
+
# The openchamber scheduled task launches at logon and locks its
|
|
726
|
+
# node_modules — kill it before reinstalling, and retry npm installs
|
|
727
|
+
# (the guest's NAT networking drops large transfers).
|
|
728
|
+
try { schtasks /End /TN dev.openchamber.web 2>&1 | Out-Null } catch {}
|
|
729
|
+
Stop-Process -Name node -Force -ErrorAction SilentlyContinue
|
|
730
|
+
function Invoke-NpmRetry([string]$argsLine) {
|
|
731
|
+
for ($try = 1; $try -le 3; $try++) {
|
|
732
|
+
cmd /c "npm $argsLine" 2>&1 | Out-Null
|
|
733
|
+
if ($LASTEXITCODE -eq 0) { return }
|
|
734
|
+
Write-Host "npm $argsLine attempt $try failed ($LASTEXITCODE); retrying"
|
|
735
|
+
Start-Sleep -Seconds 10
|
|
736
|
+
}
|
|
737
|
+
throw "npm $argsLine failed after 3 attempts"
|
|
738
|
+
}
|
|
739
|
+
Invoke-NpmRetry "install -g opencode-ai"
|
|
740
|
+
New-Item -ItemType Directory -Force -Path C:\npm-global | Out-Null
|
|
741
|
+
Invoke-NpmRetry "install -g --prefix C:\npm-global --ignore-scripts @alibaba-group/open-code-review@${var.open_code_review_version}"
|
|
742
|
+
foreach ($toolPath in @('C:\npm-global')) {
|
|
743
|
+
if ((Test-Path $toolPath) -and -not (($machinePath -split ';') -contains $toolPath)) {
|
|
744
|
+
$machinePath = $machinePath + ';' + $toolPath
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
[System.Environment]::SetEnvironmentVariable('Path', $machinePath, 'Machine')
|
|
748
|
+
Invoke-NpmRetry "install -g @openchamber/web"
|
|
749
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path','User')
|
|
750
|
+
opencode --version
|
|
751
|
+
ocr --version
|
|
752
|
+
# Web UI as a native service (Windows: scheduled task). Snapshot the
|
|
753
|
+
# absolute opencode path like the macOS template does.
|
|
754
|
+
$opencodeBin = (Get-Command opencode -ErrorAction Stop).Source
|
|
755
|
+
$env:OPENCODE_BINARY = $opencodeBin
|
|
756
|
+
# openchamber's own `startup enable` writes startup.env but then fails
|
|
757
|
+
# to register the task — its schtasks /TR command exceeds the 261-char
|
|
758
|
+
# limit and the failure aborts the whole provisioner. So: let it write
|
|
759
|
+
# the env file, ignore the failure, and register a compact task that
|
|
760
|
+
# runs a small wrapper script instead of a giant inline command.
|
|
761
|
+
try {
|
|
762
|
+
openchamber startup enable --port ${var.openchamber_port} --lan --ui-password "${var.openchamber_ui_password}" 2>$null | Out-Null
|
|
763
|
+
} catch {}
|
|
764
|
+
$LASTEXITCODE = 0
|
|
765
|
+
$envFile = 'C:\Users\Administrator\.config\openchamber\startup.env'
|
|
766
|
+
if (-not (Test-Path $envFile)) {
|
|
767
|
+
New-Item -ItemType Directory -Force -Path (Split-Path $envFile) | Out-Null
|
|
768
|
+
@"
|
|
769
|
+
OPENCHAMBER_UI_PASSWORD='${var.openchamber_ui_password}'
|
|
770
|
+
OPENCODE_BINARY='$opencodeBin'
|
|
771
|
+
"@ | Set-Content -Path $envFile -Encoding UTF8
|
|
772
|
+
}
|
|
773
|
+
New-Item -ItemType Directory -Force -Path C:\tools | Out-Null
|
|
774
|
+
$wrapper = @'
|
|
775
|
+
$envFile = 'C:\Users\Administrator\.config\openchamber\startup.env'
|
|
776
|
+
if (Test-Path $envFile) {
|
|
777
|
+
Get-Content $envFile | ForEach-Object {
|
|
778
|
+
if ($_ -match '^([^=]+)=(.*)$') {
|
|
779
|
+
$v = $matches[2]
|
|
780
|
+
if ($v.StartsWith("'") -and $v.EndsWith("'")) {
|
|
781
|
+
$v = $v.Substring(1, $v.Length - 2).Replace("'\''", "'")
|
|
782
|
+
}
|
|
783
|
+
[Environment]::SetEnvironmentVariable($matches[1], $v, 'Process')
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
& 'C:\Program Files\nodejs\node.exe' 'C:\Users\Administrator\AppData\Roaming\npm\node_modules\@openchamber\web\bin\cli.js' serve --foreground --port ${var.openchamber_port} --host 0.0.0.0
|
|
788
|
+
'@
|
|
789
|
+
Set-Content -Path C:\tools\openchamber-startup.ps1 -Value $wrapper -Encoding UTF8
|
|
790
|
+
# Register the logon task. schtasks.exe from the elevated WinRM
|
|
791
|
+
# session can be flaky (silent /Create failures, /Run "file not
|
|
792
|
+
# found"); fall back to the PowerShell-native Register-ScheduledTask
|
|
793
|
+
# (same mechanism the build-agent-images repo uses). Never fail the
|
|
794
|
+
# build here — worst case the web UI starts at first login.
|
|
795
|
+
$taskRegistered = $false
|
|
796
|
+
try {
|
|
797
|
+
schtasks /Create /TN dev.openchamber.web /SC ONLOGON /RL LIMITED /F /TR "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\tools\openchamber-startup.ps1" 2>&1 | Out-Null
|
|
798
|
+
if ($LASTEXITCODE -eq 0) {
|
|
799
|
+
schtasks /Run /TN dev.openchamber.web 2>&1 | Out-Null
|
|
800
|
+
if ($LASTEXITCODE -eq 0) { $taskRegistered = $true }
|
|
801
|
+
}
|
|
802
|
+
$LASTEXITCODE = 0
|
|
803
|
+
} catch {
|
|
804
|
+
Write-Warning "schtasks registration failed ($($_.Exception.Message)); trying Register-ScheduledTask"
|
|
805
|
+
$LASTEXITCODE = 0
|
|
806
|
+
}
|
|
807
|
+
if (-not $taskRegistered) {
|
|
808
|
+
try {
|
|
809
|
+
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NoProfile -ExecutionPolicy Bypass -File C:\tools\openchamber-startup.ps1'
|
|
810
|
+
$trigger = New-ScheduledTaskTrigger -AtLogOn
|
|
811
|
+
Register-ScheduledTask -TaskName 'dev.openchamber.web' -Action $action -Trigger $trigger -Force | Out-Null
|
|
812
|
+
Start-ScheduledTask -TaskName 'dev.openchamber.web'
|
|
813
|
+
$taskRegistered = $true
|
|
814
|
+
} catch {
|
|
815
|
+
Write-Warning "Register-ScheduledTask also failed: $($_.Exception.Message); the web UI will start at first login instead"
|
|
816
|
+
$LASTEXITCODE = 0
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if ($taskRegistered) { Write-Host "OpenChamber task registered: $taskRegistered" }
|
|
820
|
+
# Windows' firewall "TCP/UDP Query User" rules for node.exe default to
|
|
821
|
+
# BLOCK when the allow prompt goes unanswered (headless build) — the
|
|
822
|
+
# web UI then only answers on the guest loopback. Remove them and
|
|
823
|
+
# allow the UI port explicitly so the host can reach it.
|
|
824
|
+
Get-NetFirewallRule -Direction Inbound -Action Block -ErrorAction SilentlyContinue |
|
|
825
|
+
Where-Object { $_.DisplayName -like '*Query User*node.exe*' } |
|
|
826
|
+
Remove-NetFirewallRule -ErrorAction SilentlyContinue
|
|
827
|
+
New-NetFirewallRule -DisplayName 'OpenChamber' -Direction Inbound -Action Allow -Protocol TCP -LocalPort ${var.openchamber_port} -Profile Any -ErrorAction SilentlyContinue | Out-Null
|
|
828
|
+
$LASTEXITCODE = 0
|
|
829
|
+
END
|
|
830
|
+
]
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
# ===== OpenSSH Server + RDP =====
|
|
834
|
+
# sshd gives the host a management channel (agent-dev-env run windows-vmware
|
|
835
|
+
# connects to the guest's NAT IP on port 22); RDP gives a desktop session.
|
|
836
|
+
# Password auth is enabled for the Administrator account so the fixed
|
|
837
|
+
# sandbox credentials from the vars file work.
|
|
838
|
+
provisioner "powershell" {
|
|
839
|
+
elevated_user = var.winrm_username
|
|
840
|
+
elevated_password = var.winrm_password
|
|
841
|
+
inline = [<<-END
|
|
842
|
+
$ErrorActionPreference = 'Stop'
|
|
843
|
+
# Note: Add-WindowsCapability is a cmdlet — it does NOT set
|
|
844
|
+
# $LASTEXITCODE (a stale $null made the old `if ($LASTEXITCODE -ne 0)`
|
|
845
|
+
# check false-fire and abort the build even on success). Failures
|
|
846
|
+
# surface as terminating errors via -ErrorAction Stop instead.
|
|
847
|
+
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 -ErrorAction Stop
|
|
848
|
+
Set-Service -Name sshd -StartupType Automatic
|
|
849
|
+
$cfg = 'C:\ProgramData\ssh\sshd_config'
|
|
850
|
+
if (Test-Path $cfg) {
|
|
851
|
+
(Get-Content $cfg) -replace '#PasswordAuthentication yes','PasswordAuthentication yes' `
|
|
852
|
+
-replace 'Match Group administrators','#Match Group administrators' `
|
|
853
|
+
-replace ' AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys','# AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys' `
|
|
854
|
+
| Set-Content $cfg
|
|
855
|
+
}
|
|
856
|
+
# PowerShell as the default SSH shell
|
|
857
|
+
New-ItemProperty -Path 'HKLM:\SOFTWARE\OpenSSH' -Name DefaultShell -Value 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' -PropertyType String -Force | Out-Null
|
|
858
|
+
New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 | Out-Null
|
|
859
|
+
Start-Service sshd
|
|
860
|
+
# RDP
|
|
861
|
+
Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections -Value 0
|
|
862
|
+
Enable-NetFirewallRule -DisplayGroup 'Remote Desktop'
|
|
863
|
+
END
|
|
864
|
+
]
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
# ===== Bridge tooling: socat + npiperelay (best-effort) =====
|
|
868
|
+
# Used by the SSH-agent bridge on the host side of the sandbox runner
|
|
869
|
+
# (see docs/windows-vmware.md). Not load-bearing for the image itself, so
|
|
870
|
+
# a download failure only warns.
|
|
871
|
+
provisioner "powershell" {
|
|
872
|
+
elevated_user = var.winrm_username
|
|
873
|
+
elevated_password = var.winrm_password
|
|
874
|
+
inline = [<<-END
|
|
875
|
+
$ErrorActionPreference = 'Stop'
|
|
876
|
+
$ProgressPreference = 'SilentlyContinue'
|
|
877
|
+
try {
|
|
878
|
+
New-Item -ItemType Directory -Force -Path C:\tools | Out-Null
|
|
879
|
+
Invoke-WebRequest -UseBasicParsing -Uri 'https://github.com/jstarks/npiperelay/releases/download/v0.1.0/npiperelay_windows_amd64.zip' -OutFile "$env:TEMP\npiperelay.zip"
|
|
880
|
+
Expand-Archive "$env:TEMP\npiperelay.zip" -DestinationPath C:\tools -Force
|
|
881
|
+
Remove-Item "$env:TEMP\npiperelay.zip" -Force
|
|
882
|
+
Invoke-WebRequest -UseBasicParsing -Uri 'https://github.com/valorisa/socat-1.8.0.1_for_Windows/raw/main/socat.exe' -OutFile 'C:\tools\socat.exe'
|
|
883
|
+
$machinePath = [System.Environment]::GetEnvironmentVariable('Path','Machine').TrimEnd(';')
|
|
884
|
+
if (-not (($machinePath -split ';') -contains 'C:\tools')) {
|
|
885
|
+
[System.Environment]::SetEnvironmentVariable('Path', $machinePath + ';C:\tools', 'Machine')
|
|
886
|
+
}
|
|
887
|
+
Write-Host 'Bridge tooling installed: C:\tools\npiperelay.exe, C:\tools\socat.exe'
|
|
888
|
+
} catch {
|
|
889
|
+
Write-Warning "Bridge tooling install failed (bridges will be unavailable): $($_.Exception.Message)"
|
|
890
|
+
}
|
|
891
|
+
END
|
|
892
|
+
]
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
# ===== Performance & privacy hardening =====
|
|
896
|
+
provisioner "powershell" {
|
|
897
|
+
elevated_user = var.winrm_username
|
|
898
|
+
elevated_password = var.winrm_password
|
|
899
|
+
inline = [<<-END
|
|
900
|
+
$ErrorActionPreference = 'Stop'
|
|
901
|
+
# --- Disable Defender realtime scanning (speed + flakiness) ---
|
|
902
|
+
Set-MpPreference -DisableRealtimeMonitoring $true
|
|
903
|
+
reg add 'HKLM\SOFTWARE\Policies\Microsoft\Windows Defender' /v DisableAntiSpyware /t REG_DWORD /d 1 /f
|
|
904
|
+
# --- Disable heavyweight / telemetry services ---
|
|
905
|
+
$disableServices = @(
|
|
906
|
+
'WSearch', # Windows Search indexer
|
|
907
|
+
'SysMain', # Superfetch
|
|
908
|
+
'DiagTrack', # Diagnostics Tracking (telemetry)
|
|
909
|
+
'dmwappushservice',
|
|
910
|
+
'MapsBroker', # Downloaded Maps Manager
|
|
911
|
+
'lfsvc', # Geolocation
|
|
912
|
+
'RetailDemo',
|
|
913
|
+
'wisvc' # Windows Insider
|
|
914
|
+
)
|
|
915
|
+
foreach ($svc in $disableServices) {
|
|
916
|
+
$s = Get-Service $svc -ErrorAction SilentlyContinue
|
|
917
|
+
if ($s) {
|
|
918
|
+
Stop-Service $svc -Force -ErrorAction SilentlyContinue
|
|
919
|
+
Set-Service $svc -StartupType Disabled -ErrorAction SilentlyContinue
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
# --- Scheduled telemetry tasks ---
|
|
923
|
+
@(
|
|
924
|
+
'\\Microsoft\\Windows\\Application Experience\\',
|
|
925
|
+
'\\Microsoft\\Windows\\Customer Experience Improvement Program\\'
|
|
926
|
+
) | ForEach-Object {
|
|
927
|
+
Get-ScheduledTask -TaskPath $_ -ErrorAction SilentlyContinue | Disable-ScheduledTask -ErrorAction SilentlyContinue | Out-Null
|
|
928
|
+
}
|
|
929
|
+
# --- Filesystem + boot + power ---
|
|
930
|
+
fsutil behavior set disablelastaccess 1 | Out-Null
|
|
931
|
+
fsutil behavior set disable8dot3 1 | Out-Null
|
|
932
|
+
bcdedit /timeout 0 | Out-Null
|
|
933
|
+
powercfg /h off | Out-Null
|
|
934
|
+
Set-TimeZone -Id 'UTC'
|
|
935
|
+
END
|
|
936
|
+
]
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
# ===== Final verification & cleanup =====
|
|
940
|
+
provisioner "powershell" {
|
|
941
|
+
elevated_user = var.winrm_username
|
|
942
|
+
elevated_password = var.winrm_password
|
|
943
|
+
inline = [<<-END
|
|
944
|
+
$ErrorActionPreference = 'Stop'
|
|
945
|
+
Write-Host '=== Versions ==='
|
|
946
|
+
$os = Get-CimInstance Win32_OperatingSystem
|
|
947
|
+
Write-Host "Windows: $($os.Caption) build $($os.BuildNumber)"
|
|
948
|
+
Write-Host "PowerShell execution policy: $(Get-ExecutionPolicy -Scope LocalMachine)"
|
|
949
|
+
node --version
|
|
950
|
+
npm --version
|
|
951
|
+
python --version
|
|
952
|
+
git --version
|
|
953
|
+
gh --version
|
|
954
|
+
rg --version
|
|
955
|
+
jq --version
|
|
956
|
+
opencode --version
|
|
957
|
+
ocr --version
|
|
958
|
+
openchamber --version
|
|
959
|
+
docker --version
|
|
960
|
+
docker compose version
|
|
961
|
+
# New toolchains: check-and-warn. A missing helper must never fail
|
|
962
|
+
# the build — e.g. llvm-config is not shipped by every LLVM Windows
|
|
963
|
+
# build, and choco packages do not shim every binary.
|
|
964
|
+
$checkTools = @(
|
|
965
|
+
@('go', 'version'),
|
|
966
|
+
@('rustc', '--version'),
|
|
967
|
+
@('cargo', '--version'),
|
|
968
|
+
@('protoc', '--version'),
|
|
969
|
+
@('nasm', '--version'),
|
|
970
|
+
@('clang', '--version'),
|
|
971
|
+
@('x86_64-w64-mingw32-gcc', '--version')
|
|
972
|
+
)
|
|
973
|
+
foreach ($tool in $checkTools) {
|
|
974
|
+
$cmd = Get-Command $tool[0] -ErrorAction SilentlyContinue
|
|
975
|
+
if ($cmd) {
|
|
976
|
+
& $cmd.Source $tool[1]
|
|
977
|
+
if ($LASTEXITCODE -ne 0) { Write-Warning "$($tool[0]) --version failed ($LASTEXITCODE)" }
|
|
978
|
+
} else {
|
|
979
|
+
Write-Warning "$($tool[0]) not found on PATH"
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
if (Test-Path 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC') {
|
|
983
|
+
Write-Host "MSVC: $('C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC')"
|
|
984
|
+
}
|
|
985
|
+
# VMware Tools are load-bearing for the sandbox runner: vmrun
|
|
986
|
+
# getGuestIPAddress (guest IP discovery) and HGFS shared folders
|
|
987
|
+
# need the vmtoolsd service. The ARM64 tools MSI ships no VMCI
|
|
988
|
+
# driver (Fusion's windows.iso carries vmxnet3 + vm3d +
|
|
989
|
+
# vmusbmouse only), so the tools installer skips its own service
|
|
990
|
+
# registration and a plain install lands with vmtoolsd.exe present
|
|
991
|
+
# but no service (observed as the runner stuck at "Waiting for the
|
|
992
|
+
# guest IP" for 15 min). Register it here, in the final
|
|
993
|
+
# verification — NOT in autounattend.xml's FirstLogonCommands: a
|
|
994
|
+
# longer tools CommandLine (service registration inline) broke
|
|
995
|
+
# Windows Setup at first boot ("Windows could not complete the
|
|
996
|
+
# installation", Pre-OOBE windeploy 0x80220005, build hung on
|
|
997
|
+
# "Waiting for WinRM"), so the unattend command must stay small.
|
|
998
|
+
# Fail hard when the service still did not come up — such an image
|
|
999
|
+
# is not runnable, so a build that ships it must fail instead.
|
|
1000
|
+
$vmtoolsExe = 'C:\Program Files\VMware\VMware Tools\vmtoolsd.exe'
|
|
1001
|
+
$vmtoolsSvc = Get-Service 'VMware Tools' -ErrorAction SilentlyContinue
|
|
1002
|
+
if ((Test-Path $vmtoolsExe) -and -not $vmtoolsSvc) {
|
|
1003
|
+
Write-Host "Registering 'VMware Tools' service (ARM64 tools skip the VMCI driver)"
|
|
1004
|
+
New-Service -Name 'VMware Tools' -DisplayName 'VMware Tools' `
|
|
1005
|
+
-BinaryPathName "\"$vmtoolsExe\"" -StartupType Automatic | Out-Null
|
|
1006
|
+
$vmtoolsSvc = Get-Service 'VMware Tools' -ErrorAction SilentlyContinue
|
|
1007
|
+
}
|
|
1008
|
+
if ($vmtoolsSvc -and $vmtoolsSvc.Status -ne 'Running') {
|
|
1009
|
+
Start-Service 'VMware Tools' -ErrorAction SilentlyContinue
|
|
1010
|
+
$vmtoolsSvc = Get-Service 'VMware Tools' -ErrorAction SilentlyContinue
|
|
1011
|
+
}
|
|
1012
|
+
$vmtoolsd = Test-Path $vmtoolsExe
|
|
1013
|
+
if (-not $vmtoolsd -or -not $vmtoolsSvc -or $vmtoolsSvc.Status -ne 'Running') {
|
|
1014
|
+
# ASCII-only error message: non-ASCII characters in inline
|
|
1015
|
+
# PowerShell string literals are mangled in the WinRM transfer
|
|
1016
|
+
# (the em-dash came back as a smart quote, which closed the
|
|
1017
|
+
# string early and killed the whole script's parse).
|
|
1018
|
+
throw "VMware Tools are not fully installed - vmtoolsd.exe present: $vmtoolsd; 'VMware Tools' service: $($vmtoolsSvc). The sandbox runner needs the Tools service (vmrun getGuestIPAddress, HGFS); check the autounattend tools step."
|
|
1019
|
+
}
|
|
1020
|
+
Write-Host "VMware Tools: vmtoolsd.exe present, 'VMware Tools' service running ($($vmtoolsSvc.Status))"
|
|
1021
|
+
Write-Host "Chrome: $(Test-Path 'C:\Program Files\Google\Chrome\Application\chrome.exe')"
|
|
1022
|
+
Write-Host "Firefox: $(Test-Path 'C:\Program Files\Mozilla Firefox\firefox.exe')"
|
|
1023
|
+
Write-Host "VS Code: $(Test-Path "$env:LOCALAPPDATA\Programs\Microsoft VS Code\bin\code.cmd")"
|
|
1024
|
+
# Cleanup — must never fail the build: choco cleanup can exit non-zero
|
|
1025
|
+
# (locked files etc.) and that $LASTEXITCODE would otherwise propagate
|
|
1026
|
+
# as the script's exit code even though everything succeeded. The
|
|
1027
|
+
# redirect happens inside cmd so PowerShell 5.1 never turns choco's
|
|
1028
|
+
# stderr into a terminating error under $ErrorActionPreference='Stop'.
|
|
1029
|
+
cmd /c "choco cleanup -y > NUL 2>&1"
|
|
1030
|
+
$LASTEXITCODE = 0
|
|
1031
|
+
Remove-Item -Path $env:TEMP\* -Recurse -Force -ErrorAction SilentlyContinue
|
|
1032
|
+
Write-Host 'Windows sandbox image complete'
|
|
1033
|
+
END
|
|
1034
|
+
]
|
|
1035
|
+
}
|
|
1036
|
+
}
|