A HashiCorp Packer multi-component plugin for Sylve — a lightweight, open-source FreeBSD management platform for Bhyve VMs, FreeBSD Jails, and ZFS storage.
The plugin targets the Sylve REST API (base path https://<host>:8181) to create and
manage Bhyve VM images on FreeBSD 15.0+.
Follows packer-plugin-scaffolding conventions:
builder/sylve/ # Builder: creates Bhyve VM images via the Sylve API
provisioner/sylve/ # Provisioner: configures a running guest
post-processor/sylve/ # Post-processor: processes artifacts after build
datasource/sylve/ # Data source: queries existing Sylve resources
version/ # Version constants (used by goreleaser ldflags)
main.go # Plugin entry point — registers all components
Makefile # Build, install, and test targets
docs/ # MDX component docs for the Packer integration portal
example/ # HCL2 (.pkr.hcl) usage examples
# Build
go build ./...
# Build dev binary and install into Packer's plugin path
make dev
# Unit tests
go test ./...
# Acceptance tests (requires a live Sylve instance)
PACKER_ACC=1 go test -count 1 -v ./... -timeout=120m- Config:
.goreleaser.yml— cross-builds darwin/arm64 and freebsd/amd64, ldflags forversion.Version/version.VersionPrerelease,API_VERSION=x5.0(must matchpacker-plugin-sdkplugin API; bump when the SDK changes). - Git tags:
vX.Y.Z(HashiCorp style). - GitHub Actions:
.github/workflows/release.ymlruns GoReleaser onv*tags;.github/workflows/ci.ymlrunsgo teston pushes and PRs. - Local:
make goreleaser-checkvalidates the config;make goreleaser-snapshotbuilds intodist/(snapshot mode; install thegoreleaserCLI first). - Manual GitHub uploads (v0.1.x): see
docs/GITHUB_RELEASES.md; branch protection:docs/BRANCH_PROTECTION.md. - Patch release from
main:bin/create_release.sh(bumps PATCH, updatesCHANGELOG.md, commits, tagsvX.Y.Z, pushes; see development-workflow).
Acceptance tests require SYLVE_URL and SYLVE_TOKEN to be set (see Environment Variables).
All components use github.com/hashicorp/packer-plugin-sdk (>= v0.5.2):
- Decode config structs with
github.com/mitchellh/mapstructure+hcldec— always implementConfigSpec(). - Implement builder
Run()using themultisteppackage; each discrete action is its ownStep. - Reuse
multistep/commonstepshelpers (SSH communicator, boot commands, ISO steps) before writing custom steps. Prepare()must have no side effects: validate and decode config only, no API calls or resource creation.- Honor
ctx.Done()in every long-running operation; never block on cancellation.
The builder ID (BuilderId on the Artifact) must follow the format <namespace>.sylve and
must never change after the first public release, as post-processors use it to identify
compatible artifacts.
The Sylve REST API contract is defined in docs/swagger/swagger.yaml in the
Sylve repository. Keep an internal API client
under internal/client/ and avoid scattering raw HTTP calls across components.
Write all template examples in HCL2 (.pkr.hcl). Legacy JSON templates are secondary.
All coding standards, best practices, and development guidelines are defined in the .github/instructions/
directory. Every coding agent must read and follow the relevant rule files before making any
change.
| Rule File | Purpose |
|---|---|
.github/instructions/coding-agent-guidelines.instructions.md |
Mandatory: guidelines for agents creating or modifying code |
.github/instructions/general-coding-standards.instructions.md |
Universal standards for all file types (no emojis, plain ASCII) |
.github/instructions/git-commit.instructions.md |
Conventional commit format and approval workflow |
.github/instructions/license-header.instructions.md |
BSD-2-Clause header requirements per file type |
.github/instructions/shell-scripts.instructions.md |
POSIX shell, step pattern, log format |
.github/instructions/development-workflow.instructions.md |
Branch strategy, release process |
.github/instructions/test-scripts.instructions.md |
Script inventory, retry workflow |
.github/instructions/command-line-options.instructions.md |
Long-form CLI options |
.github/instructions/command-output.instructions.md |
Show complete unfiltered output |
.github/instructions/terminal.instructions.md |
Terminal usage |
.github/instructions/vulnerability-remediation.instructions.md |
Pen test remediation workflow |
.github/instructions/instruction-file-standards.instructions.md |
Standards for instruction files themselves |
- No Emojis: Never use emojis in code, config, or data files (only in
.mddocs) - Conventional Commits: All commits must follow the conventional commit format
- Long-Form Options: Use
--verbosenot-vin shell scripts and documentation - BSD-2-Clause License: All source files must have license headers
- Development Branch: All work happens on
development, never commit directly tomain - POSIX Shell: All shell scripts use
#!/bin/sh, never#!/bin/bash - Format Before Commit: Run
./bin/format_files.shbefore every commit
./bin/format_files.sh # Apply formatting to all file types
./bin/run_format_checks.sh # Verify formatting is correct
./bin/run_linter_checks.sh # Run all linters
go test ./... # Run unit testsFor a full local gate (format check, lint, unit tests with coverage, gitleaks, security scanners), run:
./bin/run_all_quality_checks.shFor manual GitHub release bundles (same layout as GoReleaser for v0.1.x tags where Actions skips GoReleaser), see
docs/GITHUB_RELEASES.md. Run ./bin/build_release_artifacts.sh to produce the zips and checksum
file; run ./bin/publish_github_release_artifacts.sh --version <X.Y.Z> to build (optional) and upload with gh.
| Variable | Description |
|---|---|
SYLVE_URL |
Sylve instance base URL, e.g. https://freebsd-host:8181 |
SYLVE_TOKEN |
API authentication token |
PACKER_ACC |
Set to 1 to enable acceptance tests |