Blueprint Structure¶
A blueprint is a directory containing a manifest file, one or more task files, and an optional lockfile.
Directory layout¶
my-blueprint/
├── manifest.yml # required: metadata, variables, task order
├── variables.yml # optional: base variable definitions
├── alloy.lock.yml # optional: resolved toolchain URLs and checksums (commit this)
├── 00-system.yml # task file 1
├── 10-toolchain.yml # task file 2
├── 20-environment.yml # task file 3
└── alloy.state.yml # auto-generated by provisioner — DO NOT commit
The numbering convention for task files (00-, 10-, 20-) is a recommendation, not a requirement. The explicit run_order: in manifest.yml controls execution order regardless of filename.
manifest.yml¶
The manifest is the entry point. The provisioner reads it first.
name: ARM Cortex-M Development Environment # required
version: 1.0.0 # required
description: ARM GCC toolchain for Cortex-M embedded development.
variables: # optional
ARM_TOOLCHAIN_DEST: /opt/arm-none-eabi
DEV_USERNAME: vagrant
toolchains: # optional: catalog references
- ref: toolchain.arm-gnu.arm-none-eabi@stable
alias: ARM_TOOLCHAIN # injects ARM_TOOLCHAIN_URL and ARM_TOOLCHAIN_SHA
required_env: # optional: validate env vars before provisioning
- name: GITLAB_TOKEN
description: Token for private artifact registry
supported_hosts: # optional: document supported platforms
- linux/amd64
- linux/arm64
run_order: # required: explicit execution order
- 00-system.yml
- 10-toolchain.yml
- 20-environment.yml
Manifest fields¶
| Field | Required | Description |
|---|---|---|
name |
Yes | Human-readable environment name |
version |
Yes | Semantic version (e.g. 1.2.0) |
description |
No | Short description |
variables |
No | Key-value pairs; available as {{.Vars.NAME}} in task files |
toolchains |
No | Catalog refs with optional aliases |
required_env |
No | Env vars that must be set before provisioning |
supported_hosts |
No | Documentation of supported os/arch platforms |
run_order |
Yes | Ordered list of task files to execute |
Task files¶
Task files are flat YAML lists. Each item is a task with a name, an action, and action-specific fields.
- name: Install build tools
action: apt_install
packages:
- git
- cmake
- ninja-build
- python3
- name: Download ARM GCC toolchain
action: unarchive_from_url
dest: "{{.Vars.ARM_TOOLCHAIN_DEST}}"
creates: "{{.Vars.ARM_TOOLCHAIN_DEST}}/bin/arm-none-eabi-gcc"
per_arch:
amd64:
url: https://example.com/arm-gcc-13-x86_64.tar.xz
sha256: "aaa111bbb222..."
arm64:
url: https://example.com/arm-gcc-13-aarch64.tar.xz
sha256: "ccc333ddd444..."
- name: Configure PATH
action: write_env_file
file: /etc/profile.d/10-arm-toolchain.sh
content: |
export PATH=$PATH:{{.Vars.ARM_TOOLCHAIN_DEST}}/bin
owner: root:root
mode: "0644"
Common task fields¶
| Field | Description |
|---|---|
name |
Required. Human-readable task identifier. |
action |
Required. The operation to perform (see below). |
creates |
Path to check; if it exists, skip the task. The fastest idempotency mechanism. |
always_run |
If true, bypass all idempotency checks and always execute. |
skip_if |
Shell expression; if it exits 0, skip the task entirely. Takes precedence over always_run. |
run_as |
Run as this user. Use {{.Vars.DEV_USERNAME}}; auto-detected from sudo context. |
owner |
Set file ownership after execution (e.g. {{.Vars.DEV_USERNAME}}:{{.Vars.DEV_USERNAME}}). |
mode |
Set file permissions after execution (e.g. 0755 or u+x). |
per_arch |
Architecture-specific overrides for url, sha256, command, or source. |
Architecture overrides (per_arch)¶
Use per_arch to handle amd64 and arm64 differences without conditional logic:
- name: Install toolchain
action: unarchive_from_url
dest: /opt/my-toolchain
creates: /opt/my-toolchain/bin/my-gcc
per_arch:
amd64:
url: https://example.com/toolchain-x86_64.tar.gz
sha256: "abc123..."
arm64:
url: https://example.com/toolchain-aarch64.tar.gz
sha256: "def456..."
The provisioner detects the host architecture and merges the matching block before executing the task.
Common actions¶
| Action | What it does |
|---|---|
apt_install |
Install packages via apt-get (only installs missing packages) |
run_command |
Execute a shell command |
get_file |
Download a file with SHA256 verification |
unarchive_from_url |
Download and extract an archive |
unarchive_from_ref |
Extract a toolchain resolved from the lockfile |
write_env_file |
Write a file (env script, config, toolchain file) |
udev_rules |
Install a udev rules file and reload udev |
python_package |
Install a Python package via pip |
rust_package |
Install a Rust package via cargo |
Full action reference: Blueprint Syntax
alloy.lock.yml¶
The lockfile pins catalog toolchain references to exact download URLs and SHA256 checksums. Generate it with:
resolved_at: "2026-01-15T10:00:00Z"
host: linux/amd64
toolchains:
toolchain.arm-gnu.arm-none-eabi@stable:
url: https://api.alloy-it.io/toolchains/arm-gnu/arm-none-eabi/stable/arm-gnu-13.3.tar.xz
sha256: "abc123def456..."
method: archive
Always commit the lockfile. It ensures every developer, CI runner, and Docker build uses byte-for-byte identical toolchains.
alloy.state.yml¶
The provisioner creates alloy.state.yml after each run. It records a fingerprint of every completed task so re-runs can skip unchanged work.
Do not commit alloy.state.yml. Add it to .gitignore:
Next steps¶
- Variables & Configuration: manifest vars, variables.yml, env files
- The Layers Approach: recommended file organization
- Blueprint Syntax Reference: all actions and fields