Imported from lightning-it/packer-vsphere-template-sources (
AGENTS.md). Install upstream withnpx skills add lightning-it/packer-vsphere-template-sources. Copyright stays with the author.
Agent Instructions
Repository Purpose
This repository builds first-install Linux template objects for VMware vSphere with HashiCorp Packer.
Supported first-install object names:
rhel-8-minimalrhel-9-minimalrhel-10-minimaltemplate-ubuntu-24-servertemplate-ubuntu-26-server
These are first-install objects for later Ansible normalization into final managed
templates such as template-rhel-8-minimal, template-rhel-9-minimal, and
template-rhel-10-minimal, template-ubuntu-24-server, and
template-ubuntu-26-server.
Common Practice Rules
- Keep this repository generic and reusable. Do not commit organization-specific values, customer hostnames, real vCenter endpoints, real datastore paths, passwords, subscription credentials, tokens, or Vault data.
- Keep secrets in ignored local var files or environment variables.
- Keep plugin and tool versions pinned. Do not introduce floating version
constraints such as
>=,~>, orlatestfor Packer plugins unless the user explicitly asks for that behavior. - Keep RHEL 8, RHEL 9, and RHEL 10 behavior parameterized from the same Packer source unless there is a real OS-specific reason to split files.
- Keep Ubuntu Server 24.04 and 26.04 behavior parameterized from the same Packer source unless there is a real release-specific reason to split files.
- Keep
breakglassas the default temporary installer account unless the user explicitly requests a different account. - Use SSH key authentication for Packer guest access. Do not add SSH password authentication for the temporary installer account.
- Install
open-vm-toolsduring OS installation so VMware guest operations work on first boot. - Keep the Packer-built objects minimal. Final managed users, SSH keys, identity cleanup, and final vSphere template readiness belong to the Ansible template bootstrap workflow.
- Keep RHEL install automation in Kickstart and Ubuntu install automation in autoinstall/cloud-init seed data.
Packer Tooling
Use HashiCorp Packer only. On some RHEL systems /usr/sbin/packer is a
Cracklib utility, not HashiCorp Packer.
Before running Packer commands, check:
command -v packer
test "$(command -v packer)" != "/usr/sbin/packer"
packer version
If HashiCorp Packer is not installed, either install it outside the repo or use
a temporary binary under /tmp for validation. Do not vendor downloaded Packer
binaries into this repository.
Validation
Before finishing changes, run the repository test entry point:
scripts/test-packer.sh
The script is the source of truth for the Packer verification suite. It checks
that packer is HashiCorp Packer, syntax-checks the build wrapper scripts,
runs packer init, verifies formatting with packer fmt -check -recursive .,
and validates the full supported OS matrix:
- RHEL 8, RHEL 9, and RHEL 10 with source names
rhel-<major>-minimal - Ubuntu 24.04 and Ubuntu 26.04 with source names
template-ubuntu-<major>-source
Also run whitespace checks before finishing:
git diff --check
The pre-commit configuration runs scripts/test-packer.sh for changes to
Packer HCL, installer HTTP templates, example variables, and build/test
scripts. When changing the test matrix or validation policy, update
scripts/test-packer.sh, .pre-commit-config.yaml, and this section together.
Heavy Build Verification
A real Packer build is a heavy integration test. It is gated to run only for
the develop -> main branch flow or manual workflow dispatch. The automated
GitHub Actions path runs on a self-hosted Ubuntu runner with Incus and nested
virtualization labels, creates a temporary nested ESXi VM, runs Packer against
that standalone ESXi API endpoint, and destroys the Incus VM afterward.
It requires a private prepared ESXi Incus image, private runner access, datastore/network placement, reachable ISO media, and enough time for an unattended OS install. It creates or refreshes the vSphere source image object, so do not run it from normal pre-commit.
Use the heavy test wrapper for an intentional image build:
PACKER_SOURCE_BRANCH=develop \
PACKER_TARGET_BRANCH=main \
PACKER_BUILD_KIND=rhel \
PACKER_BUILD_VERSION=8 \
PACKER_BUILD_VM_NAME=rhel-8-minimal \
PACKER_VAR_FILE=vars/local.pkrvars.hcl \
scripts/test-packer-heavy.sh
For Ubuntu:
PACKER_SOURCE_BRANCH=develop \
PACKER_TARGET_BRANCH=main \
PACKER_BUILD_KIND=ubuntu \
PACKER_BUILD_VERSION=24.04 \
PACKER_BUILD_VM_NAME=template-ubuntu-24-source \
PACKER_VAR_FILE=vars/local.pkrvars.hcl \
scripts/test-packer-heavy.sh
The same heavy build is available as a manual pre-commit hook:
PACKER_SOURCE_BRANCH=develop \
PACKER_TARGET_BRANCH=main \
PACKER_BUILD_KIND=rhel \
PACKER_BUILD_VERSION=8 \
PACKER_VAR_FILE=vars/local.pkrvars.hcl \
pre-commit run packer-heavy-build --hook-stage manual
The wrapper checks PACKER_SOURCE_BRANCH and PACKER_TARGET_BRANCH first.
It also recognizes GitHub Actions GITHUB_HEAD_REF/GITHUB_BASE_REF and
GitLab merge request variables. If the branch flow is anything other than
develop -> main, the heavy test exits successfully with a skip message.
The public repository owns the component entry point and guarded caller in
.github/workflows/packer-heavy-nested-esxi.yml. GitHub Actions orchestration
is delegated to the SHA-pinned reusable Packer profile in modulix-validation;
do not add infrastructure execution steps to the caller. The Incus VM lifecycle
is owned by the lit.supplementary.incus_nested_esxi collection role and
invoked through .github/playbooks/nested-esxi.yml; keep reusable Incus/ESXi
lifecycle logic in that role. Keep the ESXi Incus image alias, ESXi
credentials, ISO paths, checksums, and installer passwords in GitHub Actions
secrets or repository variables.
Required runner labels:
self-hosted, linux, x64, ubuntu, incus, nested-virt
Required secrets:
PACKER_NESTED_ESXI_USERNAMEPACKER_NESTED_ESXI_PASSWORDPACKER_INSTALLER_PASSWORDPACKER_INSTALLER_PASSWORD_HASHfor Ubuntu builds
Required or commonly used repository variables:
PACKER_NESTED_ESXI_INCUS_IMAGEPACKER_NESTED_ESXI_ENDPOINTPACKER_NESTED_ESXI_CPUPACKER_NESTED_ESXI_MEMORYPACKER_NESTED_ESXI_ROOT_DISK_SIZEPACKER_NESTED_ESXI_RAW_QEMUPACKER_NESTED_ESXI_DATASTOREPACKER_NESTED_ESXI_NETWORKPACKER_RHEL8_ISO_PATHPACKER_RHEL8_ISO_CHECKSUMPACKER_RHEL9_ISO_PATHPACKER_RHEL9_ISO_CHECKSUMPACKER_RHEL10_ISO_PATHPACKER_RHEL10_ISO_CHECKSUMPACKER_UBUNTU2404_ISO_PATHPACKER_UBUNTU2404_ISO_CHECKSUMPACKER_UBUNTU2604_ISO_PATHPACKER_UBUNTU2604_ISO_CHECKSUMPACKER_INSTALLER_USERNAME
In comparison with Ansible tests, packer validate is like an Ansible syntax
and input-contract check: it proves the template can be loaded and the required
variables are coherent. packer build is closer to a Molecule or integration
run against real infrastructure: it performs the install, creates the vSphere
object, and can fail because of external state such as ISO paths, vCenter
permissions, network reachability, datastore capacity, or guest boot timing.
If the host packer command is not HashiCorp Packer, report that limitation.
Use a temporary official Packer binary when practical, but do not vendor Packer
binaries into this repository.
File Ownership
- Packer plugin pinning belongs in
packer.pkr.hcl. - Shared vSphere builder locals belong in
shared.pkr.hcl. - RHEL vSphere builder behavior belongs in
rhel.pkr.hcl. - Ubuntu vSphere builder behavior belongs in
ubuntu.pkr.hcl. - Inputs and defaults belong in
variables.pkr.hcl. - Kickstart content belongs in
installer-data/rhel/ks.cfg.pkrtpl.hcl. - Ubuntu autoinstall content belongs in
installer-data/ubuntu/. - Operator examples belong in
vars/example.pkrvars.hcl. - Local operator var files must remain ignored by Git.
Editing Style
- Use ASCII unless an existing file clearly requires otherwise.
- Keep shell scripts POSIX-friendly where possible, and use Bash intentionally when arrays or strict mode are needed.
- Prefer explicit variables and clear validation over hidden environment assumptions.
- Keep README examples runnable with the repository layout.
REP-60 current-revision review governance
- Local validation is deterministic only. It must never invoke Codex, GitHub Copilot, another model, or an external AI endpoint. Authoritative AI review runs only in the protected GitHub pipeline and binds the exact PR head.
- Lightning IT automation may request and fund one GitHub Copilot review only
when the exact PR author is
litroc, and only at the finalization boundary; intermediatesynchronizepushes must not trigger AI review. Any finding requires correction and a final current-head re-review. The request is consumed once per head; unavailable or quota-blocked reviews fail closed without an automatic retry. Organization-funded Codex remediation and its single re-review are likewise restricted tolitroc. - Every other human or external contributor supplies any required current-head Copilot review under their own entitlement and cost. Lightning IT verifies valid evidence but never requests or funds that review, and personal tokens or provider keys never enter Actions.
- A same-repository PR authored exactly by
lightning-it-release-automation[bot]uses only the protected MLX-90 ยง7.2 Exact-Revision Codex check. It must never request Copilot or synthesize a Copilot success. - A proven ancestry-only main-to-develop backmerge uses the deterministic evidence-bound exemption and performs zero AI calls. Unknown automation identities fail closed.
- The only neutral merge-gate result is
Current revision review. Missing, stale, ambiguous, or unresolved review evidence blocks the merge.
REP-60 evidence lifecycle (mandatory)
- Every pull request into
developretains its exact-final-head native GitHub CI, required-check, and review history as the authoritative evidence for acceptance intodevelop. - A pull request into
developMUST NOT create or retain an additional durable release-evidence package, duplicate WORM artifact, or second AI-review evidence outside that native GitHub history. - Only the protected
developtomainpromotion creates exactly one durable, complete release-evidence package. It binds the full integrated promotion diff, base, head, merge base, integration tree, policy, reviewer result, and all release and audit checks. - Agents, workflows, and repository-local rules MUST NOT duplicate that durable
evidence per
developpull request or invoke local AI to create evidence. Repository-local rules may only make this lifecycle stricter.
Devtools container execution boundary
- Every deterministic lint, format, type-check, test, build, packaging, policy, and validation workload runs in the digest-pinned Lightning IT Devtools image, locally and in CI. Host-language runtimes never provide acceptance evidence.
- The host boundary is limited to Git, the supported container engine, and the centrally managed Devtools, push-ready, and pre-commit dispatchers. A dispatcher may inspect Git state and start the pinned container, but it must not execute a repository validator through host Python, Node.js, Ansible, Ruff, a Python type checker, markdownlint, Renovate, or a comparable host runtime.
- If a required command or compatible version is absent, fail closed. Add and
pin it in
container-ee-wunder-devtools-ubi9, release that image normally, update the centrally managed digest, and rerun the gate. Host fallbacks, ad-hoc virtual environments, and unpinned helper images are forbidden. - Repository-owned tests derive the exact full Devtools image reference from the centrally managed push-ready engine when checking the installed wrapper; they never hard-code an independent release tag that can drift during a normal image rollout.
- A target-specific regression test that asserts managed Devtools-wrapper arguments is the same atomic managed unit as the wrapper. Both synchronize through an exact source binding, and only an explicitly digest-allowlisted predecessor may be replaced; unknown target test content fails closed.
- Defaults stay read-only, offline, socket-free, capability-dropped, and
non-privileged. A gate may opt into only its explicit tested minimum. Linked
Git metadata remains read-only and container Git may trust only
/workspace, never*. Executable temporary fixtures use the isolated container home while generic/tmpremains non-executable. - The Devtools boundary never makes local Codex, Copilot, or other model calls and never receives personal AI credentials.
AI model and token governance
Apply LIT-GEN-GDR-GOV-30-Budget-Conscious-AI-Model-Selection to every
substantive Codex or ChatGPT-assisted task. Before investigation, planning, tool
use, implementation, or delegation, record a compact task profile in the task
chat: work item, risk (low, normal, or high), smallest sufficient
model/reasoning choice, rationale, and a concrete escalation condition.
- Use the balanced, lowest reliable capability by default. Escalate to a premium/frontier model or higher reasoning only for a high-risk decision, complex architecture/debugging/dependencies, or a documented focused failure of the standard approach. Restrict that escalation to the difficult subtask.
- Never use Speed Mode. Do not replace verification with a more expensive model or sacrifice quality to reduce elapsed time.
- Retrieve only relevant issue, files, logs, and source records; avoid broad repository or chat-history loading, speculative analysis, and unbounded retry loops. Delegate only independent, bounded work that reduces total effort.
- For GitHub or Jira work, include the task profile in the issue/task record when AI assistance materially affects execution. Close with verification and remaining risks; preserve durable decisions in Confluence, Jira, or GitHub.