Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/gitleaks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: gitleaks
on:
pull_request:
push:
branches:
- main
workflow_dispatch:
schedule:
- cron: "0 4 * * *" # run once a day at 4 AM
Expand Down
49 changes: 33 additions & 16 deletions .github/workflows/test-build-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ jobs:
if: github.event_name == 'pull_request' || inputs.enable_utm_qemu_macos
name: build ${{ matrix.go_test_name }} VM_OS ${{ matrix.vm_os }} runner_os ${{ matrix.runs_on }}
runs-on: ${{ matrix.runs_on }}
env:
DEV_ALCHEMY_APP_DATA_DIR: ${{ github.workspace }}/.dev-alchemy
DEV_ALCHEMY_CACHE_DIR: ${{ github.workspace }}/.dev-alchemy/cache
DEV_ALCHEMY_VAGRANT_DIR: ${{ github.workspace }}/.dev-alchemy/.vagrant
DEV_ALCHEMY_PACKER_CACHE_DIR: ${{ github.workspace }}/.dev-alchemy/packer_cache
PACKER_CACHE_DIR: ${{ github.workspace }}/.dev-alchemy/packer_cache
timeout-minutes: 260
strategy:
fail-fast: false
Expand All @@ -39,43 +45,43 @@ jobs:
deploy_test_name: TestDeployUtmWindows11Arm64OnMacos
runs_on: macos-26-tart
cache_files: >-
[{"local-path":"./cache/windows11/iso/win11_25h2_english_arm64.iso","container":"build-cache"},
{"local-path":"./cache/utm/utm-guest-tools-latest.iso"},
{"local-path":"./cache/windows/virtio-win.iso"},
{"local-path":"./cache/qemu-efi-aarch64_all.deb"}]
[{"local-path":"./.dev-alchemy/cache/windows11/iso/win11_25h2_english_arm64.iso","container":"build-cache"},
{"local-path":"./.dev-alchemy/cache/utm/utm-guest-tools-latest.iso"},
{"local-path":"./.dev-alchemy/cache/windows/virtio-win.iso"},
{"local-path":"./.dev-alchemy/cache/qemu-efi-aarch64_all.deb"}]
- vm_os: windows11
go_test_name: TestBuildQemuWindows11Amd64OnMacos
deploy_test_name: TestDeployUtmWindows11Amd64OnMacos
runs_on: macos-26-tart
cache_files: >-
[{"local-path":"./cache/windows11/iso/win11_25h2_english_amd64.iso","container":"build-cache"},
{"local-path":"./cache/utm/utm-guest-tools-latest.iso"}]
[{"local-path":"./.dev-alchemy/cache/windows11/iso/win11_25h2_english_amd64.iso","container":"build-cache"},
{"local-path":"./.dev-alchemy/cache/utm/utm-guest-tools-latest.iso"}]
- vm_os: ubuntu
go_test_name: TestBuildQemuUbuntuServerArm64OnMacos
deploy_test_name: TestDeployUtmUbuntuServerArm64OnMacos
runs_on: macos-26-tart
cache_files: >-
[{"local-path":"./cache/linux/ubuntu-24.04.3-live-server-arm64.iso"},
{"local-path":"./cache/qemu-efi-aarch64_all.deb"}]
[{"local-path":"./.dev-alchemy/cache/linux/ubuntu-24.04.3-live-server-arm64.iso"},
{"local-path":"./.dev-alchemy/cache/qemu-efi-aarch64_all.deb"}]
- vm_os: ubuntu
go_test_name: TestBuildQemuUbuntuServerAmd64OnMacos
deploy_test_name: TestDeployUtmUbuntuServerAmd64OnMacos
runs_on: macos-26-tart
cache_files: >-
[{"local-path":"./cache/linux/ubuntu-24.04.3-live-server-amd64.iso"}]
[{"local-path":"./.dev-alchemy/cache/linux/ubuntu-24.04.3-live-server-amd64.iso"}]
- vm_os: ubuntu
go_test_name: TestBuildQemuUbuntuDesktopArm64OnMacos
deploy_test_name: TestDeployUtmUbuntuDesktopArm64OnMacos
runs_on: macos-26-tart
cache_files: >-
[{"local-path":"./cache/linux/ubuntu-24.04.3-live-server-arm64.iso"},
{"local-path":"./cache/qemu-efi-aarch64_all.deb"}]
[{"local-path":"./.dev-alchemy/cache/linux/ubuntu-24.04.3-live-server-arm64.iso"},
{"local-path":"./.dev-alchemy/cache/qemu-efi-aarch64_all.deb"}]
- vm_os: ubuntu
go_test_name: TestBuildQemuUbuntuDesktopAmd64OnMacos
deploy_test_name: TestDeployUtmUbuntuDesktopAmd64OnMacos
runs_on: macos-26-tart
cache_files: >-
[{"local-path":"./cache/linux/ubuntu-24.04.3-live-server-amd64.iso"}]
[{"local-path":"./.dev-alchemy/cache/linux/ubuntu-24.04.3-live-server-amd64.iso"}]
steps:
- name: Checkout code
uses: actions/checkout@v6
Expand Down Expand Up @@ -116,11 +122,11 @@ jobs:
go mod vendor
sudo -E make test-build-specific TEST_NAME=${{ matrix.go_test_name }}

- name: Normalize cache ownership after sudo build
- name: Normalize managed app-data ownership after sudo build
if: always() && contains(matrix.runs_on, 'macos')
run: |
if [ -d ./cache ]; then
sudo chown -R "$(id -u):$(id -g)" ./cache
if [ -d ./.dev-alchemy ]; then
sudo chown -R "$(id -u):$(id -g)" ./.dev-alchemy
fi

- name: Run deploy smoke test TEST_NAME=${{ matrix.deploy_test_name }}
Expand All @@ -138,7 +144,18 @@ jobs:
if: contains(matrix.runs_on, 'macos')
run: |
mkdir -p artifact
find ./cache -name '*.vnc.mp4' -exec cp {} artifact/ \;
if [ -d ./.dev-alchemy/cache ]; then
find ./.dev-alchemy/cache -name '*.vnc.mp4' -exec cp {} artifact/ \;
fi

- name: Upload Playwright diagnostics artifact (always)
if: always() && contains(matrix.runs_on, 'macos')
uses: actions/upload-artifact@v7
with:
name: playwright-diagnostics-${{ matrix.vm_os }}-${{ matrix.go_test_name }}
path: ./.dev-alchemy/cache/windows/playwright-diagnostics/**
if-no-files-found: ignore
retention-days: 7

- name: Upload packer-qemu-${{ matrix.vm_os }}-${{ matrix.go_test_name }}.vnc.mp4 artifact (always)
if: contains(matrix.runs_on, 'macos')
Expand Down
18 changes: 14 additions & 4 deletions .github/workflows/test-build-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ jobs:
needs: [provision-hyperv-runner]
timeout-minutes: 60
env:
DEV_ALCHEMY_APP_DATA_DIR: ${{ github.workspace }}\.dev-alchemy
DEV_ALCHEMY_CACHE_DIR: ${{ github.workspace }}\.dev-alchemy\cache
DEV_ALCHEMY_VAGRANT_DIR: ${{ github.workspace }}\.dev-alchemy\.vagrant
DEV_ALCHEMY_PACKER_CACHE_DIR: ${{ github.workspace }}\.dev-alchemy\packer_cache
PACKER_CACHE_DIR: ${{ github.workspace }}\.dev-alchemy\packer_cache
PACKER_TEMP_PATH: "D:\\hyperv-temp" # Use Azure VM temp disk for packer temp files to speed up build
runs-on:
- self-hosted
Expand All @@ -124,7 +129,7 @@ jobs:
- name: Download build cache files
uses: ./.github/actions/download-build-cache
with:
files: '[{"local-path":"./cache/windows11/iso/win11_25h2_english_amd64.iso","container":"build-cache"}]'
files: '[{"local-path":"./.dev-alchemy/cache/windows11/iso/win11_25h2_english_amd64.iso","container":"build-cache"}]'
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Install dependencies windows
Expand Down Expand Up @@ -172,7 +177,7 @@ jobs:
- name: Upload build cache files
uses: ./.github/actions/upload-build-cache
with:
files: '[{"local-path":"./cache/windows11/iso/win11_25h2_english_amd64.iso","container":"build-cache"}]'
files: '[{"local-path":"./.dev-alchemy/cache/windows11/iso/win11_25h2_english_amd64.iso","container":"build-cache"}]'
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Fail if build or deploy smoke test failed
Expand Down Expand Up @@ -298,6 +303,11 @@ jobs:
needs: [provision-virtualbox-runner]
timeout-minutes: 80
env:
DEV_ALCHEMY_APP_DATA_DIR: ${{ github.workspace }}\.dev-alchemy
DEV_ALCHEMY_CACHE_DIR: ${{ github.workspace }}\.dev-alchemy\cache
DEV_ALCHEMY_VAGRANT_DIR: ${{ github.workspace }}\.dev-alchemy\.vagrant
DEV_ALCHEMY_PACKER_CACHE_DIR: ${{ github.workspace }}\.dev-alchemy\packer_cache
PACKER_CACHE_DIR: ${{ github.workspace }}\.dev-alchemy\packer_cache
PACKER_TEMP_PATH: "D:\\vbox-temp" # Use Azure VM temp disk for packer temp files to speed up build
runs-on:
- self-hosted
Expand All @@ -320,7 +330,7 @@ jobs:
- name: Download build cache files
uses: ./.github/actions/download-build-cache
with:
files: '[{"local-path":"./cache/windows11/iso/win11_25h2_english_amd64.iso","container":"build-cache"}]'
files: '[{"local-path":"./.dev-alchemy/cache/windows11/iso/win11_25h2_english_amd64.iso","container":"build-cache"}]'
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Install dependencies windows
Expand Down Expand Up @@ -358,7 +368,7 @@ jobs:
- name: Upload build cache files
uses: ./.github/actions/upload-build-cache
with:
files: '[{"local-path":"./cache/windows11/iso/win11_25h2_english_amd64.iso","container":"build-cache"}]'
files: '[{"local-path":"./.dev-alchemy/cache/windows11/iso/win11_25h2_english_amd64.iso","container":"build-cache"}]'
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Fail if Packer build failed
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-cmd-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: Test Go Unit Tests

on:
push:
branches:
- main
paths:
- "cmd/cmd/**"
- ".github/workflows/test-cmd-unit-tests.yml"
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-deploy-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: Test Deploy and Provision Unit Tests

on:
push:
branches:
- main
paths:
- "pkg/deploy/**"
- "pkg/provision/**"
Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,22 @@ To force a VM rebuild even when the cached build artifact already exists, use:
go run cmd/main.go build windows11 --arch amd64 --no-cache
```

#### Managed application data

VM build and deployment state is now stored outside the repository in an OS-appropriate app-data directory:

- macOS: `~/Library/Application Support/dev-alchemy`
- Windows: `%LOCALAPPDATA%\dev-alchemy`
- Linux: `${XDG_DATA_HOME:-~/.local/share}/dev-alchemy`

Under that root, Dev Alchemy manages:

- `cache/` for downloaded files and build artifacts
- `.vagrant/` for isolated Vagrant state
- `packer_cache/` for Packer plugin/download cache

You can override the default location by setting `DEV_ALCHEMY_APP_DATA_DIR`. Dev Alchemy also exports `DEV_ALCHEMY_CACHE_DIR`, `DEV_ALCHEMY_VAGRANT_DIR`, and `DEV_ALCHEMY_PACKER_CACHE_DIR` for helper scripts and manual workflows.



##### Enable ansible remote access on Windows
Expand Down Expand Up @@ -343,7 +359,7 @@ go run cmd/main.go install

You will need a Windows .iso file to use as the installation media for your virtual machine. You can download a Windows 10 or Windows Server .iso file from the Microsoft website.

Or use script to download a Windows 11 .iso file: [download_win_11.ps1](./scripts/windows/download_win_11.ps1)
Or use script to download a Windows 11 `.iso` file: [download_win_11.ps1](./scripts/windows/download_win_11.ps1). By default it stores the ISO under the managed cache directory described above.

##### Build a Windows VM

Expand Down
14 changes: 12 additions & 2 deletions build/packer/linux/mint/linux-mint-hyperv.pkr.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,19 @@ variable "iso_checksum" {
default = "sha256:759c9b5a2ad26eb9844b24f7da1696c705ff5fe07924a749f385f435176c2306"
}

variable "cache_dir" {
type = string
default = env("DEV_ALCHEMY_CACHE_DIR")
description = "Managed cache directory outside the repository."
validation {
condition = var.cache_dir != ""
error_message = "The cache_dir variable must be set, typically via DEV_ALCHEMY_CACHE_DIR."
}
}

source "hyperv-iso" "linuxmint" {
vm_name = "linux-mint-packer-${formatdate("YYYY-MM-DD-hh-mm", timestamp())}"
output_directory = "${path.root}/../../../../cache/linux/hyperv-mint-output-${formatdate("YYYY-MM-DD-hh-mm", timestamp())}"
output_directory = "${var.cache_dir}/linux/hyperv-mint-output-${formatdate("YYYY-MM-DD-hh-mm", timestamp())}"

iso_url = var.iso_url
iso_checksum = var.iso_checksum
Expand Down Expand Up @@ -64,7 +74,7 @@ build {
}

post-processor "vagrant" {
output = "${path.root}/../../../../cache/windows/linux-mint-hyperv.box"
output = "${var.cache_dir}/windows/linux-mint-hyperv.box"
keep_input_artifact = false
provider_override = "hyperv"
compression_level = 1
Expand Down
15 changes: 11 additions & 4 deletions build/packer/linux/ubuntu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,25 @@ Both Hyper-V variants use cloud-init apt offline mode (`fallback: offline-instal
Manual Packer usage:

```powershell
$AppDataDir = if ($env:DEV_ALCHEMY_APP_DATA_DIR) { $env:DEV_ALCHEMY_APP_DATA_DIR } else { Join-Path $env:LOCALAPPDATA "dev-alchemy" }
$CacheDir = Join-Path $AppDataDir "cache"
$env:DEV_ALCHEMY_CACHE_DIR = $CacheDir
$env:DEV_ALCHEMY_PACKER_CACHE_DIR = Join-Path $AppDataDir "packer_cache"
$isoPath = Join-Path $CacheDir "linux\ubuntu-24.04.3-live-server-amd64.iso"

packer init build/packer/linux/ubuntu/linux-ubuntu-hyperv.pkr.hcl

# server
packer build -var "ubuntu_type=server" -var "iso_url=./cache/linux/ubuntu-24.04.3-live-server-amd64.iso" build/packer/linux/ubuntu/linux-ubuntu-hyperv.pkr.hcl
packer build -var "cache_dir=$CacheDir" -var "ubuntu_type=server" -var "iso_url=$isoPath" build/packer/linux/ubuntu/linux-ubuntu-hyperv.pkr.hcl

# desktop
packer build -var "ubuntu_type=desktop" -var "iso_url=./cache/linux/ubuntu-24.04.3-live-server-amd64.iso" build/packer/linux/ubuntu/linux-ubuntu-hyperv.pkr.hcl
packer build -var "cache_dir=$CacheDir" -var "ubuntu_type=desktop" -var "iso_url=$isoPath" build/packer/linux/ubuntu/linux-ubuntu-hyperv.pkr.hcl
```

Output boxes:

- `cache/ubuntu/hyperv-ubuntu-server-amd64.box`
- `cache/ubuntu/hyperv-ubuntu-desktop-amd64.box`
- `%LOCALAPPDATA%\dev-alchemy\cache\ubuntu\hyperv-ubuntu-server-amd64.box`
- `%LOCALAPPDATA%\dev-alchemy\cache\ubuntu\hyperv-ubuntu-desktop-amd64.box`

## Build Ubuntu on macOS Hosts (UTM/QEMU)

Expand All @@ -54,6 +60,7 @@ go run cmd/main.go build ubuntu --type desktop --arch $arch
Manual script usage:

```bash
export DEV_ALCHEMY_APP_DATA_DIR="${DEV_ALCHEMY_APP_DATA_DIR:-$HOME/Library/Application Support/dev-alchemy}"
build/packer/linux/ubuntu/linux-ubuntu-on-macos.sh --project-root "$PWD" --arch amd64 --ubuntu-type server
build/packer/linux/ubuntu/linux-ubuntu-on-macos.sh --project-root "$PWD" --arch amd64 --ubuntu-type desktop
```
14 changes: 12 additions & 2 deletions build/packer/linux/ubuntu/linux-ubuntu-hyperv.pkr.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ variable "switch_name" {
default = "Default Switch"
}

variable "cache_dir" {
type = string
default = env("DEV_ALCHEMY_CACHE_DIR")
description = "Managed cache directory outside the repository."
validation {
condition = var.cache_dir != ""
error_message = "The cache_dir variable must be set, typically via DEV_ALCHEMY_CACHE_DIR."
}
}

locals {
default_ubuntu_iso_url = "https://releases.ubuntu.com/${var.ubuntu_version}/ubuntu-${var.ubuntu_version}-live-server-amd64.iso"
effective_iso_url = var.iso_url != "" ? var.iso_url : local.default_ubuntu_iso_url
Expand All @@ -55,8 +65,8 @@ locals {
"<wait2>",
"<f10><wait>",
]
output_directory = "${path.root}/../../../../cache/linux/hyperv-ubuntu-${var.ubuntu_type}-output-${formatdate("YYYY-MM-DD-hh-mm", timestamp())}"
box_output = "${path.root}/../../../../cache/ubuntu/hyperv-ubuntu-${var.ubuntu_type}-amd64.box"
output_directory = "${var.cache_dir}/linux/hyperv-ubuntu-${var.ubuntu_type}-output-${formatdate("YYYY-MM-DD-hh-mm", timestamp())}"
box_output = "${var.cache_dir}/ubuntu/hyperv-ubuntu-${var.ubuntu_type}-amd64.box"
}

source "hyperv-iso" "ubuntu" {
Expand Down
22 changes: 19 additions & 3 deletions build/packer/linux/ubuntu/linux-ubuntu-on-macos.pkr.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,26 @@ variable "memory" {
description = "Memory in MB to allocate to the VM"
}

variable "cache_dir" {
type = string
default = env("DEV_ALCHEMY_CACHE_DIR")
description = "Managed cache directory outside the repository."
validation {
condition = var.cache_dir != ""
error_message = "The cache_dir variable must be set, typically via DEV_ALCHEMY_CACHE_DIR."
}
}

variable "build_output_dir" {
type = string
default = ""
description = "Optional short-lived Packer output directory to avoid long UNIX socket paths on macOS."
}

locals {
iso_url = var.iso_url
ubuntu_iso_checksum = var.arch == "amd64" ? "sha256:c3514bf0056180d09376462a7a1b4f213c1d6e8ea67fae5c25099c6fd3d8274b" : "none"
cache_directory = "${path.root}/../../../../cache"
cache_directory = var.cache_dir
boot_command = {
"amd64" = [
"e<wait2>",
Expand Down Expand Up @@ -100,7 +116,7 @@ locals {
["-accel", var.is_ci ? "tcg,thread=multi,tb-size=512" : "hvf"],
["-machine", "virt,highmem=on"],
["-cpu", var.is_ci ? "max,sve=off,pauth-impdef=on" : "host"],
["-bios", "${path.root}/../../../../cache/qemu-uefi/usr/share/qemu-efi-aarch64/QEMU_EFI.fd"],
["-bios", "${local.cache_directory}/qemu-uefi/usr/share/qemu-efi-aarch64/QEMU_EFI.fd"],
["-device", "ramfb"],
["-smp", "cpus=${var.cpus},cores=${var.cpus},sockets=1,threads=1"],
["-global", "PIIX4_PM.disable_s3=1"],
Expand All @@ -122,7 +138,7 @@ locals {
]
}
left_list = join("", [for i in range(0, 16) : "<left>"])
output_directory = "${local.cache_directory}/ubuntu/qemu-out-ubuntu-${var.ubuntu_type}-${var.arch}"
output_directory = var.build_output_dir != "" ? var.build_output_dir : "${local.cache_directory}/ubuntu/qemu-out-ubuntu-${var.ubuntu_type}-${var.arch}"
}

source "qemu" "ubuntu" {
Expand Down
Loading
Loading