Ensure the Go toolchain is installed.
Add a file to .ops/main.go, for example:
package main
import (
"context""fmt""lesiw.io/ops"
)
typeOpsstruct{}
funcmain() { ops.Handle(Ops{}) }
func (Ops) Hello(ctx context.Context) error {
fmt.Println("Hello world!")
returnnil
}Then use op to run it.
go install lesiw.io/op@latest # Install op.
op -l # => hello
op hello # => Hello world!You can also play with a basic example on the Go playground.
The context provided by the framework is canceled after the op completes,
making context.AfterFunc a natural way to register cleanup:
func (oOps) Deploy(ctx context.Context) error {
env, err:=createEnvironment()
iferr!=nil {
returnerr
}
context.AfterFunc(ctx, func() {
env.Cleanup()
})
returnrunTests(env)
}Ops can call each other directly:
func (oOps) All(ctx context.Context) error {
iferr:=o.Build(ctx); err!=nil {
returnerr
}
returno.Test(ctx)
}ops.Defer(func()) will run func() at the end of the program regardless of
whether an op completes successfully.
ops.After(func()) will run func() after all ops have completed successfully.
By reflecting on a type, ops.Handle(any) allows the use of embedding to
compose ops from multiple modules together. ops.Handle(any) follows the same
rules as selectors in the Go spec with one notable exception:
multiple methods with the same name at the same depth will be run sequentially
in the same order as their embedded types.