Documentation
¶
Index ¶
- Constants
- Variables
- func Connect(service, port string) string
- func ConnectEnode(service, id string) string
- func ConnectRaw(service, port, protocol, user string) string
- func ConnectWs(service, port string) string
- func CreatePrometheusServices(manifest *Manifest, out *output) error
- func DownloadRelease(outputFolder string, artifact *release) (string, error)
- func GenerateCustomRecipeToDir(customRecipeName, targetDir string) (string, error)
- func GenerateDocs(recipes []Recipe) error
- func GenerateFromCustomRecipe(customRecipeName string, force bool) error
- func GetEmbeddedCustomRecipes() ([]string, error)
- func GetHomeDir() (string, error)
- func GetLocalSessions() ([]string, error)
- func GetRecipeComponents(recipe Recipe) []string
- func GetRecipeComponentsFormatted(recipe Recipe) string
- func GetSessionServices(session string) ([]string, error)
- func Inspect(ctx context.Context, serviceName, portName string) error
- func IsYAMLRecipeFile(path string) bool
- func Logs(ctx context.Context, sessionName, serviceName string, follow bool) error
- func NewOutput(dst string) (*output, error)
- func RecipeToYAML(recipe Recipe) (string, error)
- func SendTestTransaction(ctx context.Context, cfg *TestTxConfig) error
- func StopSession(id string, keepResources bool) error
- func UseHealthmon(component *Component, s *Service, chain string)
- type ArtifactsBuilder
- func (b *ArtifactsBuilder) ApplyLatestL1Fork(applyLatestL1Fork bool) *ArtifactsBuilder
- func (b *ArtifactsBuilder) ApplyLatestL2Fork(applyLatestL2Fork *uint64) *ArtifactsBuilder
- func (b *ArtifactsBuilder) Build(out *output) error
- func (b *ArtifactsBuilder) GenesisDelay(genesisDelaySeconds uint64) *ArtifactsBuilder
- func (b *ArtifactsBuilder) L1BlockTime(blockTimeSeconds uint64) *ArtifactsBuilder
- func (b *ArtifactsBuilder) OpBlockTime(blockTimeSeconds uint64) *ArtifactsBuilder
- func (b *ArtifactsBuilder) PredeployFile(filePath string) *ArtifactsBuilder
- func (b *ArtifactsBuilder) PrefundedAccounts(accounts []string) *ArtifactsBuilder
- func (b *ArtifactsBuilder) WithExtraFile(artifactName, sourcePath string) *ArtifactsBuilder
- func (b *ArtifactsBuilder) WithL2() *ArtifactsBuilder
- type BProxy
- type BootnodeRef
- type BuilderHub
- type BuilderNetRecipe
- func (b *BuilderNetRecipe) Apply(ctx *ExContext) *Component
- func (b *BuilderNetRecipe) Artifacts() *ArtifactsBuilder
- func (b *BuilderNetRecipe) Description() string
- func (b *BuilderNetRecipe) Flags() *flag.FlagSet
- func (b *BuilderNetRecipe) Name() string
- func (b *BuilderNetRecipe) Output(manifest *Manifest) map[string]interface{}
- type Callback
- type ChainMonitor
- type ClProxy
- type Component
- type ComponentGen
- type Contender
- type ContenderContext
- type CustomRecipeInfo
- type DependsOn
- type DependsOnCondition
- type EnodeAddr
- type ExContext
- type FlashblocksRPC
- type HealthCheckResponse
- type InteractiveDisplay
- type L1Recipe
- type LighthouseBeaconNode
- type LighthouseValidator
- type LocalRunner
- type LogLevel
- type Manifest
- func (m *Manifest) ApplyOverrides(overrides map[string]string) error
- func (m *Manifest) ExecutePostHookActions() error
- func (s *Manifest) GenerateDotGraph() string
- func (s *Manifest) GenerateMermaidGraph() string
- func (s *Manifest) GetService(name string) (*Service, bool)
- func (s *Manifest) MustGetService(name string) *Service
- func (s *Manifest) NewService(name string) *Service
- func (m *Manifest) SaveJson(out *output) error
- func (s *Manifest) Validate(out *output) error
- type MapStringFlag
- type MevBoost
- type MevBoostRelay
- type NodeRef
- type OpBatcher
- type OpGenesisTmplInput
- type OpGeth
- type OpNode
- type OpRbuilder
- type OpRecipe
- type OpReth
- type Port
- type ReadyCheck
- type Recipe
- type RethEL
- type RollupBoost
- type RunnerConfig
- type Service
- func (s *Service) DependsOnHealthy(name string) *Service
- func (s *Service) DependsOnRunning(name string) *Service
- func (s *Service) GetPort(name string) (*Port, bool)
- func (s *Service) GetPorts() []*Port
- func (s *Service) MustGetPort(name string) *Port
- func (s *Service) UseHostExecution() *Service
- func (s *Service) WithArgs(args ...string) *Service
- func (s *Service) WithArtifact(localPath, artifactName string) *Service
- func (s *Service) WithEntrypoint(entrypoint string) *Service
- func (s *Service) WithEnv(key, value string) *Service
- func (s *Service) WithImage(image string) *Service
- func (s *Service) WithLabel(key, value string) *Service
- func (s *Service) WithPort(name string, portNumber int, protocolVar ...string) *Service
- func (s *Service) WithPostHook(hook *postHook) *Service
- func (s *Service) WithReady(check ReadyCheck) *Service
- func (s *Service) WithRelease(rel *release) *Service
- func (s *Service) WithTag(tag string) *Service
- func (s *Service) WithUngracefulShutdown() *Service
- func (s *Service) WithVolume(name, localPath string) *Service
- type ServiceReady
- type TaskStatus
- type TestTxConfig
- type WebsocketProxy
- type YAMLComponentConfig
- type YAMLRecipe
- type YAMLRecipeConfig
- type YAMLReleaseConfig
- type YAMLServiceConfig
Constants ¶
const ( ProtocolUDP = "udp" ProtocolTCP = "tcp" )
Variables ¶
var Components = []ComponentGen{}
var CustomRecipesFS fs.FS
CustomRecipesFS holds the embedded custom recipes filesystem, set by main package
var MinimumGenesisDelay uint64 = 10
minimumGenesisDelay is the minimum delay for the genesis time. This is required because lighthouse takes some time to start and we need to make sure it is ready otherwise, some blocks are missed.
Functions ¶
func ConnectEnode ¶
func ConnectRaw ¶
func DownloadRelease ¶
func GenerateCustomRecipeToDir ¶
GenerateCustomRecipeToDir extracts a custom recipe and its dependencies to the specified directory Returns the path to the generated playground.yaml file
func GenerateDocs ¶
func GenerateFromCustomRecipe ¶
GenerateFromCustomRecipe extracts a custom recipe and its dependencies to current directory customRecipeName should be in the format "dir/filename" (e.g., "rbuilder/custom") If force is false, it will error if any files already exist
func GetEmbeddedCustomRecipes ¶
GetEmbeddedCustomRecipes returns a list of custom recipe names from the embedded custom recipes Custom recipe names can be: - "name" for recipes directly under custom-recipes/ (e.g., custom-recipes/foo.yaml -> "foo") - "dir/name" for recipes in subdirectories (e.g., custom-recipes/rbuilder/bin.yaml -> "rbuilder/bin")
func GetHomeDir ¶
func GetLocalSessions ¶
func GetRecipeComponents ¶
GetRecipeComponents returns the component names for a recipe
func GetRecipeComponentsFormatted ¶
GetRecipeComponentsFormatted returns a formatted string of component names for a recipe If a component is itself a recipe (ends with "-recipe"), it formats as "base + extra1, extra2"
func GetSessionServices ¶
func IsYAMLRecipeFile ¶
IsYAMLRecipeFile checks if the given path looks like a YAML recipe file
func RecipeToYAML ¶
RecipeToYAML converts a recipe to a playground.yaml format
func SendTestTransaction ¶
func SendTestTransaction(ctx context.Context, cfg *TestTxConfig) error
SendTestTransaction sends a test transaction and waits for the receipt
func StopSession ¶
func UseHealthmon ¶
Types ¶
type ArtifactsBuilder ¶
type ArtifactsBuilder struct {
// contains filtered or unexported fields
}
func NewArtifactsBuilder ¶
func NewArtifactsBuilder() *ArtifactsBuilder
func (*ArtifactsBuilder) ApplyLatestL1Fork ¶
func (b *ArtifactsBuilder) ApplyLatestL1Fork(applyLatestL1Fork bool) *ArtifactsBuilder
func (*ArtifactsBuilder) ApplyLatestL2Fork ¶
func (b *ArtifactsBuilder) ApplyLatestL2Fork(applyLatestL2Fork *uint64) *ArtifactsBuilder
func (*ArtifactsBuilder) Build ¶
func (b *ArtifactsBuilder) Build(out *output) error
func (*ArtifactsBuilder) GenesisDelay ¶
func (b *ArtifactsBuilder) GenesisDelay(genesisDelaySeconds uint64) *ArtifactsBuilder
func (*ArtifactsBuilder) L1BlockTime ¶
func (b *ArtifactsBuilder) L1BlockTime(blockTimeSeconds uint64) *ArtifactsBuilder
func (*ArtifactsBuilder) OpBlockTime ¶
func (b *ArtifactsBuilder) OpBlockTime(blockTimeSeconds uint64) *ArtifactsBuilder
func (*ArtifactsBuilder) PredeployFile ¶
func (b *ArtifactsBuilder) PredeployFile(filePath string) *ArtifactsBuilder
func (*ArtifactsBuilder) PrefundedAccounts ¶
func (b *ArtifactsBuilder) PrefundedAccounts(accounts []string) *ArtifactsBuilder
func (*ArtifactsBuilder) WithExtraFile ¶
func (b *ArtifactsBuilder) WithExtraFile(artifactName, sourcePath string) *ArtifactsBuilder
func (*ArtifactsBuilder) WithL2 ¶
func (b *ArtifactsBuilder) WithL2() *ArtifactsBuilder
type BProxy ¶
type BootnodeRef ¶
func (*BootnodeRef) Connect ¶
func (b *BootnodeRef) Connect() string
type BuilderHub ¶
func (*BuilderHub) Apply ¶
func (b *BuilderHub) Apply(ctx *ExContext) *Component
type BuilderNetRecipe ¶
type BuilderNetRecipe struct {
// contains filtered or unexported fields
}
BuilderNetRecipe is a recipe that extends the L1 recipe to include builder-hub
func (*BuilderNetRecipe) Apply ¶
func (b *BuilderNetRecipe) Apply(ctx *ExContext) *Component
func (*BuilderNetRecipe) Artifacts ¶
func (b *BuilderNetRecipe) Artifacts() *ArtifactsBuilder
func (*BuilderNetRecipe) Description ¶
func (b *BuilderNetRecipe) Description() string
func (*BuilderNetRecipe) Flags ¶
func (b *BuilderNetRecipe) Flags() *flag.FlagSet
func (*BuilderNetRecipe) Name ¶
func (b *BuilderNetRecipe) Name() string
func (*BuilderNetRecipe) Output ¶
func (b *BuilderNetRecipe) Output(manifest *Manifest) map[string]interface{}
type Callback ¶
type Callback func(serviceName string, update TaskStatus)
type ChainMonitor ¶
func (*ChainMonitor) Apply ¶
func (c *ChainMonitor) Apply(ctx *ExContext) *Component
type Component ¶
func NewComponent ¶
func (*Component) AddComponent ¶
func (p *Component) AddComponent(ctx *ExContext, gen ComponentGen)
func (*Component) AddService ¶
func (p *Component) AddService(srv ComponentGen)
func (*Component) NewService ¶
func (*Component) RunContenderIfEnabled ¶
type ComponentGen ¶
type Contender ¶
type ContenderContext ¶
type ContenderContext struct {
// Run `contender spam` automatically once all playground services are running.
Enabled bool
// Provide additional args to contender's CLI.
ExtraArgs []string
// Override the default target chain for contender to spam.
TargetChain string
}
func (*ContenderContext) Contender ¶
func (cc *ContenderContext) Contender() *Contender
Converts a `ContenderContext` into a `Contender` service. `Enabled` is ignored.
type CustomRecipeInfo ¶
type CustomRecipeInfo struct {
Name string
Description string
Base string
ModifiedComponents []string
NewComponents []string
}
CustomRecipeInfo contains metadata about a custom recipe
func GetCustomRecipeInfo ¶
func GetCustomRecipeInfo(customRecipeName string, baseRecipes []Recipe) (*CustomRecipeInfo, error)
GetCustomRecipeInfo returns metadata about a specific custom recipe
type DependsOn ¶
type DependsOn struct {
Name string
Condition DependsOnCondition
}
type DependsOnCondition ¶
type DependsOnCondition string
const ( DependsOnConditionRunning DependsOnCondition = "service_started" DependsOnConditionHealthy DependsOnCondition = "service_healthy" )
type EnodeAddr ¶
type EnodeAddr struct {
PrivKey *ecdsa.PrivateKey
Artifact string
}
func (*EnodeAddr) PrivKeyHex ¶
type ExContext ¶
type ExContext struct {
LogLevel LogLevel
// This dependency is not ideal. Doing it so that I do not
// have to modify the serviceDesc interface to give services
// access to the output.
Output *output
// Bootnode reference for EL nodes.
// TODO: Extend for CL nodes too
Bootnode *BootnodeRef
Contender *ContenderContext
}
Execution context
type FlashblocksRPC ¶
type FlashblocksRPC struct {
FlashblocksWSService string
BaseOverlay bool
UseWebsocketProxy bool // Whether to add /ws path for websocket proxy
}
func (*FlashblocksRPC) Apply ¶
func (f *FlashblocksRPC) Apply(ctx *ExContext) *Component
type HealthCheckResponse ¶
func ExecuteHealthCheckManually ¶
func ExecuteHealthCheckManually(serviceName string) (*HealthCheckResponse, error)
type InteractiveDisplay ¶
type InteractiveDisplay struct {
// contains filtered or unexported fields
}
func NewInteractiveDisplay ¶
func NewInteractiveDisplay(manifest *Manifest) *InteractiveDisplay
func (*InteractiveDisplay) HandleUpdate ¶
func (i *InteractiveDisplay) HandleUpdate(serviceName string, status TaskStatus)
type L1Recipe ¶
type L1Recipe struct {
// contains filtered or unexported fields
}
func (*L1Recipe) Artifacts ¶
func (l *L1Recipe) Artifacts() *ArtifactsBuilder
func (*L1Recipe) Description ¶
type LighthouseBeaconNode ¶
func (*LighthouseBeaconNode) Apply ¶
func (l *LighthouseBeaconNode) Apply(ctx *ExContext) *Component
type LighthouseValidator ¶
type LighthouseValidator struct {
BeaconNode string
}
func (*LighthouseValidator) Apply ¶
func (l *LighthouseValidator) Apply(ctx *ExContext) *Component
type LocalRunner ¶
type LocalRunner struct {
// contains filtered or unexported fields
}
LocalRunner is a component that runs the services from the manifest on the local host machine. By default, it uses docker and docker compose to run all the services. But, some services (if they are configured to do so) can be run on the host machine instead. When running inside docker, each service will use the port numbers they define in the component description. Besides, they will also bind to an available public port on the host machine. If the service runs on the host, it will use the host port numbers instead directly.
func NewLocalRunner ¶
func NewLocalRunner(cfg *RunnerConfig) (*LocalRunner, error)
func (*LocalRunner) ExitErr ¶
func (d *LocalRunner) ExitErr() <-chan error
func (*LocalRunner) Stop ¶
func (d *LocalRunner) Stop(keepResources bool) error
func (*LocalRunner) WaitForReady ¶
func (d *LocalRunner) WaitForReady(ctx context.Context) error
type Manifest ¶
type Manifest struct {
ID string `json:"session_id"`
// list of Services
Services []*Service `json:"services"`
// contains filtered or unexported fields
}
Manifest describes a list of services and their dependencies
func NewManifest ¶
func ReadManifest ¶
func (*Manifest) ApplyOverrides ¶
func (*Manifest) ExecutePostHookActions ¶
func (*Manifest) GenerateDotGraph ¶
func (*Manifest) GenerateMermaidGraph ¶
func (*Manifest) MustGetService ¶
func (*Manifest) NewService ¶
type MapStringFlag ¶
func (*MapStringFlag) Set ¶
func (n *MapStringFlag) Set(s string) error
func (*MapStringFlag) String ¶
func (n *MapStringFlag) String() string
func (*MapStringFlag) Type ¶
func (n *MapStringFlag) Type() string
type MevBoostRelay ¶
func (*MevBoostRelay) Apply ¶
func (m *MevBoostRelay) Apply(ctx *ExContext) *Component
type NodeRef ¶
type NodeRef struct {
Service string `json:"service"`
PortLabel string `json:"port_label"`
Protocol string `json:"protocol"`
User string `json:"user"`
}
NodeRef describes a reference from one service to another
type OpGenesisTmplInput ¶
type OpRbuilder ¶
type OpRbuilder struct {
Flashblocks bool
}
func (*OpRbuilder) Apply ¶
func (o *OpRbuilder) Apply(ctx *ExContext) *Component
type OpRecipe ¶
type OpRecipe struct {
// contains filtered or unexported fields
}
OpRecipe is a recipe that deploys an OP stack
func (*OpRecipe) Artifacts ¶
func (o *OpRecipe) Artifacts() *ArtifactsBuilder
func (*OpRecipe) Description ¶
type Port ¶
type Port struct {
// Name is the name of the port
Name string `json:"name"`
// Port is the port number
Port int `json:"port"`
// Protocol (tcp or udp)
Protocol string
// HostPort is the port number assigned on the host machine for this
// container port. It is populated by the local runner
// TODO: We might want to move this to the runner itself.
HostPort int
}
Port describes a port that a service exposes
type ReadyCheck ¶
type Recipe ¶
type RollupBoost ¶
type RollupBoost struct {
ELNode string
Builder string
Flashblocks bool
FlashblocksBuilderURL string
}
func (*RollupBoost) Apply ¶
func (r *RollupBoost) Apply(ctx *ExContext) *Component
type RunnerConfig ¶
type RunnerConfig struct {
Out *output
Manifest *Manifest
BindHostPortsLocally bool
NetworkName string
Labels map[string]string
LogInternally bool
Platform string
Callbacks []Callback
}
func (*RunnerConfig) AddCallback ¶
func (r *RunnerConfig) AddCallback(c Callback)
type Service ¶
type Service struct {
Name string `json:"name"`
Args []string `json:"args"`
Labels map[string]string `json:"labels,omitempty"`
// list of environment variables to set for the service
Env map[string]string `json:"env,omitempty"`
ReadyCheck *ReadyCheck `json:"ready_check,omitempty"`
DependsOn []*DependsOn `json:"depends_on,omitempty"`
Ports []*Port `json:"ports,omitempty"`
NodeRefs []*NodeRef `json:"node_refs,omitempty"`
FilesMapped map[string]string `json:"files_mapped,omitempty"`
VolumesMapped map[string]string `json:"volumes_mapped,omitempty"`
Tag string `json:"tag,omitempty"`
Image string `json:"image,omitempty"`
Entrypoint string `json:"entrypoint,omitempty"`
HostPath string `json:"host_path,omitempty"`
UngracefulShutdown bool `json:"ungraceful_shutdown,omitempty"`
// contains filtered or unexported fields
}
func (*Service) DependsOnHealthy ¶
func (*Service) DependsOnRunning ¶
func (*Service) MustGetPort ¶
func (*Service) UseHostExecution ¶
func (*Service) WithArtifact ¶
func (*Service) WithEntrypoint ¶
func (*Service) WithPostHook ¶
func (*Service) WithReady ¶
func (s *Service) WithReady(check ReadyCheck) *Service
func (*Service) WithRelease ¶
func (*Service) WithUngracefulShutdown ¶
func (*Service) WithVolume ¶
type ServiceReady ¶
type TaskStatus ¶
type TaskStatus string
var ( TaskStatusPulling TaskStatus = "pulling" TaskStatusPulled TaskStatus = "pulled" TaskStatusPending TaskStatus = "pending" TaskStatusStarted TaskStatus = "started" TaskStatusDie TaskStatus = "die" TaskStatusHealthy TaskStatus = "healthy" TaskStatusUnhealty TaskStatus = "unhealthy" )
type TestTxConfig ¶
type TestTxConfig struct {
RPCURL string // Target RPC URL for sending transactions (e.g., rbuilder)
ELRPCURL string // EL RPC URL for chain queries (e.g., reth). If empty, uses RPCURL
PrivateKey string
ToAddress string
Value *big.Int
GasLimit uint64
GasPrice *big.Int
Timeout time.Duration // Timeout for waiting for receipt. If 0, defaults to 2 minutes
}
TestTxConfig holds configuration for the test transaction
func DefaultTestTxConfig ¶
func DefaultTestTxConfig() *TestTxConfig
DefaultTestTxConfig returns the default test transaction configuration Sends from second prefunded account to first prefunded account (builder/coinbase)
type WebsocketProxy ¶
type WebsocketProxy struct {
Upstream string
}
func (*WebsocketProxy) Apply ¶
func (w *WebsocketProxy) Apply(ctx *ExContext) *Component
type YAMLComponentConfig ¶
type YAMLComponentConfig struct {
// Remove indicates whether to remove this component
Remove bool `yaml:"remove,omitempty"`
// Services is a map of service name to service config
Services map[string]*YAMLServiceConfig `yaml:"services,omitempty"`
}
YAMLComponentConfig represents a component in the YAML recipe
type YAMLRecipe ¶
type YAMLRecipe struct {
// contains filtered or unexported fields
}
YAMLRecipe wraps a base recipe and applies YAML-based modifications
func ParseYAMLRecipe ¶
func ParseYAMLRecipe(filePath string, baseRecipes []Recipe) (*YAMLRecipe, error)
ParseYAMLRecipe parses a YAML recipe file and returns a YAMLRecipe
func (*YAMLRecipe) Apply ¶
func (y *YAMLRecipe) Apply(ctx *ExContext) *Component
func (*YAMLRecipe) Artifacts ¶
func (y *YAMLRecipe) Artifacts() *ArtifactsBuilder
func (*YAMLRecipe) Description ¶
func (y *YAMLRecipe) Description() string
func (*YAMLRecipe) Flags ¶
func (y *YAMLRecipe) Flags() *flag.FlagSet
func (*YAMLRecipe) Name ¶
func (y *YAMLRecipe) Name() string
func (*YAMLRecipe) Output ¶
func (y *YAMLRecipe) Output(manifest *Manifest) map[string]interface{}
type YAMLRecipeConfig ¶
type YAMLRecipeConfig struct {
// Base is the name of the base recipe (l1, opstack, buildernet)
Base string `yaml:"base"`
// Description is an optional description of the recipe
Description string `yaml:"description,omitempty"`
// Recipe contains the component/service hierarchy to apply as overrides or additions
Recipe map[string]*YAMLComponentConfig `yaml:"recipe"`
}
YAMLRecipeConfig represents the structure of a YAML recipe file
type YAMLReleaseConfig ¶
type YAMLReleaseConfig struct {
Name string `yaml:"name"`
Org string `yaml:"org"`
Repo string `yaml:"repo,omitempty"`
Version string `yaml:"version"`
// Format specifies the download format: "tar.gz" (default) or "binary"
// For "binary", downloads the raw binary directly without extraction
Format string `yaml:"format,omitempty"`
}
YAMLReleaseConfig specifies a GitHub release to download
type YAMLServiceConfig ¶
type YAMLServiceConfig struct {
// Remove indicates whether to remove this service
Remove bool `yaml:"remove,omitempty"`
// Image is the docker image to use
Image string `yaml:"image,omitempty"`
// Tag is the docker image tag
Tag string `yaml:"tag,omitempty"`
// Entrypoint overrides the container entrypoint
Entrypoint string `yaml:"entrypoint,omitempty"`
// Args are the arguments to pass to the service
Args []string `yaml:"args,omitempty"`
// Env is a map of environment variables
Env map[string]string `yaml:"env,omitempty"`
// Ports is a map of port name to port number
Ports map[string]int `yaml:"ports,omitempty"`
// Files is a map of container path to file source
// File source can be:
// - "artifact:<name>" to reference a runtime-generated artifact (e.g., "artifact:genesis.json")
// - A relative path to a file in the same directory as the YAML recipe file
Files map[string]string `yaml:"files,omitempty"`
// Volumes is a map of container path to volume name
Volumes map[string]string `yaml:"volumes,omitempty"`
// DependsOn is a list of services this service depends on
// Format: "service_name" or "service_name:condition" where condition is "healthy" or "running"
DependsOn []string `yaml:"depends_on,omitempty"`
// HostPath is the path to the binary on the host to run instead of Docker
// When set, the service runs on the host machine instead of in a container
HostPath string `yaml:"host_path,omitempty"`
// Release specifies a GitHub release to download for host execution
Release *YAMLReleaseConfig `yaml:"release,omitempty"`
}
YAMLServiceConfig represents a service configuration in the YAML recipe