Packer: From Versioned Inputs to Typed Observations
The Packer integration exposes image-building commands and artifact observations. Its result has two independent dimensions: whether the process completed successfully and whether the emitted artifact report was complete enough to use.
The integration also shows a real help-derived positional gap and a boolean flag whose exact syntax affects automatic output selection.
Acquisition and generated commands
PackerVersionCollector discovers versions. The Design entry point composes a version image/container pipeline, PackerHelpParser with -h and a cached replay pipeline. Declared environment variables are passed to the runner through PackerEnvironmentCatalog.
Help snapshots supply generated commands to Contracts and builders/client to the implementation. They describe invocation syntax, not artifact messages.
The descriptor normally uses single-hyphen flags and equals-form values. BareTrueFlags makes machine-readable true an intentional exception.
Machine-readable selection is exact
MachineReadable = true produces -machine-readable. Explicit false produces -machine-readable=false, and an unset property produces no token.
PackerOutputStrategies.Default enables machine-readable parsing only when the serialized invocation contains the exact bare token. This matches the upstream global-mode extraction; -machine-readable=true should not be substituted casually.
Explicit MachineReadable/HumanReadable strategies select interpretation but do not modify the invocation to request a format.
A complete artifact example
This offline example uses the generated client and the explicit positional adapter. Its source-shaped artifact rows execute no Packer binary.
using FrenchExDev.Net.BinaryWrapper;
using FrenchExDev.Net.BinaryWrapper.Lib;
using FrenchExDev.Net.BinaryWrapper.Testing;
using FrenchExDev.Net.Packer;
var binding = new BinaryBinding
{
Identifier = new BinaryIdentifier("packer"),
ExecutablePath = "unused",
DetectedVersion = SemanticVersion.Parse("1.16.0"),
};
var command = await Packer.Create(binding).BuildAsync(
builder => builder.WithMachineReadable(true));
var invocation = new PackerInvocation(command, "image.pkr.hcl");
var rows = new[]
{
"1,amazon-ebs.image,artifact-count,1",
"1,amazon-ebs.image,artifact,0,builder-id,mitchellh.amazonebs",
"1,amazon-ebs.image,artifact,0,id,eu-west-1:ami-123",
"1,amazon-ebs.image,artifact,0,string,An AMI",
"1,amazon-ebs.image,artifact,0,files-count,0",
"1,amazon-ebs.image,artifact,0,end",
};
var executor = new CommandExecutor(new DictionaryBinaryResolver(binding),
new FakeProcessRunner(rows.Select(
text => new OutputLine(text, OutputSource.StdOut))));
await foreach (var evt in executor.StreamAsync(
binding.Identifier, invocation, PackerOutputStrategies.Default))
if (evt is PackerArtifactIdObserved artifact)
Console.WriteLine($"{artifact.BuildName}: {artifact.ArtifactId}");
var collected = await executor.ExecuteAsync(binding.Identifier, invocation,
PackerOutputStrategies.Default, new PackerBuildCollector());
Console.WriteLine(collected.Match(
onSuccess: result =>
$"exit={result.ExitCode}, complete={result.ArtifactReportComplete}",
onFailure: error => error.ToString()));using FrenchExDev.Net.BinaryWrapper;
using FrenchExDev.Net.BinaryWrapper.Lib;
using FrenchExDev.Net.BinaryWrapper.Testing;
using FrenchExDev.Net.Packer;
var binding = new BinaryBinding
{
Identifier = new BinaryIdentifier("packer"),
ExecutablePath = "unused",
DetectedVersion = SemanticVersion.Parse("1.16.0"),
};
var command = await Packer.Create(binding).BuildAsync(
builder => builder.WithMachineReadable(true));
var invocation = new PackerInvocation(command, "image.pkr.hcl");
var rows = new[]
{
"1,amazon-ebs.image,artifact-count,1",
"1,amazon-ebs.image,artifact,0,builder-id,mitchellh.amazonebs",
"1,amazon-ebs.image,artifact,0,id,eu-west-1:ami-123",
"1,amazon-ebs.image,artifact,0,string,An AMI",
"1,amazon-ebs.image,artifact,0,files-count,0",
"1,amazon-ebs.image,artifact,0,end",
};
var executor = new CommandExecutor(new DictionaryBinaryResolver(binding),
new FakeProcessRunner(rows.Select(
text => new OutputLine(text, OutputSource.StdOut))));
await foreach (var evt in executor.StreamAsync(
binding.Identifier, invocation, PackerOutputStrategies.Default))
if (evt is PackerArtifactIdObserved artifact)
Console.WriteLine($"{artifact.BuildName}: {artifact.ArtifactId}");
var collected = await executor.ExecuteAsync(binding.Identifier, invocation,
PackerOutputStrategies.Default, new PackerBuildCollector());
Console.WriteLine(collected.Match(
onSuccess: result =>
$"exit={result.ExitCode}, complete={result.ArtifactReportComplete}",
onFailure: error => error.ToString()));The streamed and collected calls are separate simulated executions, each with new parser state.
PackerInvocation belongs to Contracts. It appends positional arguments absent from current scraped schemas while preserving command path, target version and environment. It can support template-bearing build/init/validate/inspect patterns without discarding the generated options.
Wire records, UI and artifact fields
The machine-readable envelope carries timestamp, build target, record kind and fields. Reviewed kinds include UI, artifact counts and fields, version data and inspection-related observations.
Artifact events expose build name, artifact index, builder ID, image/artifact identity, human description, file counts/files and end markers. Unknown plugin fields keep their wire representation.
A build target is not automatically a unique globally deployed resource. Keep artifact relationships explicit and use the emitted indices/counts instead of guessing that every row belongs to the last seen image.
Human-readable parsing recognizes reviewed build milestones and diagnostics. Those observations remain source-specific; arbitrary plugin text and future formats are generic.
Completeness is a postcondition
PackerBuildCollector validates artifact indices, counts and report structure. A zero file count is a valid report; omitted file counts, conflicting fields or invalid indices can make a report incomplete.
An artifact ID alone does not establish report completeness. A success-looking message also does not establish the enclosing process's zero exit. The collected result keeps these facts distinguishable.
An application consuming an artifact can require both terminal success and a complete artifact identity. Those are distinct checks on the same execution.
Environment
PackerEnvironment configures cache/configuration directories, plugin paths and port ranges, logging and other declared settings. Numeric and boolean converters use Packer's wire forms.
The catalogue's inspected baseline for these declarations is 1.10.0. Output support for 1.11.2 or 1.16.0 is a separate boundary. A configured environment is serialized using the command target, not inferred from an artifact message.
Plugin configuration can change the emitted payload without changing the Packer core version. Preserve unknown artifact fields and track plugin compatibility independently when a workflow requires their semantics.
Output profiles and validation
Built-in profiles cover stable patches [1.11.2, 1.11.3-0) and [1.16.0, 1.16.1-0). Prereleases, unknown versions and unreviewed releases use raw fallback and terminal status.
Tests exercise core records, artifact parameters, report completeness, exact flag selection, multiline inspection, channel provenance, profile edges and nonzero actual exit. The source guide distinguishes fake transport from bounded official-binary version/inspection checks.
The wrapper reports artifact data and report completeness. Choosing an artifact for a follow-up operation remains application logic.
Source snapshot
Source inspection: 2 October 2026. These implementation paths are relative to Net/FrenchExDev in the local ecosystem checkout. The v2 changes are largely uncommitted; these paths identify inspected code, without claiming that public branch links contain it.
Packer/src/FrenchExDev.Net.Packer.Design/Program.csPacker/src/FrenchExDev.Net.Packer.Contracts/FrenchExDev.Net.Packer.Contracts.csprojPacker/src/FrenchExDev.Net.Packer/FrenchExDev.Net.Packer.csprojPacker/doc/TYPED-OUTPUT.mdPacker/test/FrenchExDev.Net.Packer.Tests
Previous: Git · Implementation index · Series index · Next: Podman