README
¶
cobra-lambda
Run your Cobra CLI applications in AWS Lambda and invoke them remotely as if they were running locally on your machine.
What is cobra-lambda?
cobra-lambda bridges the gap between traditional command-line applications and serverless functions. It provides:
- A Go library (wrapper package) - Wrap any Cobra CLI application to run in AWS Lambda with full stdout/stderr capture
- A CLI client - Invoke remote Lambda-hosted CLI applications from your terminal with familiar CLI arguments
- Seamless output handling - Captures and returns all output (stdout, stderr, and Cobra output) as structured data
Perfect for running CLI tools, automation scripts, or administrative commands serverless without maintaining long-running servers.
Installation
CLI Client (for invoking remote Lambda functions)
Install the cobra-lambda CLI tool to invoke remote Cobra applications hosted in Lambda:
go install github.com/JayJamieson/cobra-lambda/cmd/clctl@latest
Or build from source:
git clone https://github.com/JayJamieson/cobra-lambda.git
cd cobra-lambda
go build -o clctl cmd/clctl/main.go
Go Library (for wrapping Cobra apps in Lambda)
Add the wrapper package to your Lambda function project:
go get github.com/JayJamieson/cobra-lambda/wrapper
Quick Start
1. Wrap Your Cobra App for Lambda
Create a Lambda handler that wraps your Cobra CLI:
package main
import (
"context"
"encoding/json"
"github.com/aws/aws-lambda-go/lambda"
"github.com/JayJamieson/cobra-lambda/wrapper"
"github.com/spf13/cobra"
)
func Handle(ctx context.Context, event json.RawMessage) (any, error) {
// Parse arguments from Lambda event
var args []string
if err := json.Unmarshal(event, &args); err != nil {
return nil, err
}
// Create your Cobra command
var name string
cmd := &cobra.Command{
Use: "myapp",
Run: func(cmd *cobra.Command, args []string) {
cmd.Printf("Hello, %s!\n", name)
},
}
cmd.Flags().StringVar(&name, "name", "World", "Name to greet")
// Wrap and execute
w := wrapper.NewCobraLambda(cmd)
output, err := w.Execute(args)
if err != nil {
return nil, err
}
// Return structured output
return map[string]any{
"stdout": output.Output,
}, nil
}
func main() {
lambda.Start(Handle)
}
Build and deploy to Lambda:
GOOS=linux GOARCH=amd64 go build -o bootstrap main.go
zip function.zip bootstrap
# Deploy to AWS Lambda with runtime: provided.al2
2. Invoke from Your Terminal
Use the CLI client to invoke your Lambda-hosted Cobra app:
# Basic invocation
clctl --name my-lambda-function
# With arguments and flags
clctl --name my-lambda-function --name Alice
# Pass any Cobra CLI arguments
clctl --name my-lambda-function subcommand --flag value arg1 arg2
The CLI forwards all arguments after --name [function-name] to your Lambda function.
Usage Examples
Basic Command
package main
import (
"github.com/JayJamieson/cobra-lambda/wrapper"
"github.com/spf13/cobra"
)
func example() {
cmd := &cobra.Command{
Use: "greet",
Run: func(cmd *cobra.Command, args []string) {
cmd.Println("Hello from Cobra!")
},
}
w := wrapper.NewCobraLambda(context.TODO(), cmd)
output, err := w.Execute([]string{})
if err != nil {
panic(err)
}
// output.Output contains all captured output
}
With Subcommands
rootCmd := &cobra.Command{Use: "root"}
subCmd := &cobra.Command{
Use: "deploy",
Run: func(cmd *cobra.Command, args []string) {
cmd.Println("Deploying...")
},
}
rootCmd.AddCommand(subCmd)
w := wrapper.NewCobraLambda(context.TODO(), rootCmd)
output, err := w.Execute([]string{"deploy"})
Output Structure
The OutputCapture struct contains all captured output in a single field:
type OutputCapture struct {
// Output contains all captured output from Cobra, stdout, and stderr
Output string
}
All output streams (Cobra's SetOut/SetErr, os.Stdout, and os.Stderr) are captured into a single shared buffer, preserving the order of output as it was written.
Thread Safety
The wrapper is thread-safe:
- Each
CobraWrapperinstance uses a mutex to serializeExecute()calls - Internal buffers are thread-safe with their own locks
- os.Stdout/os.Stderr are restored after each execution, even on panic
For concurrent executions, create separate wrapper instances per goroutine, or reuse a single instance (executions will be serialized automatically).
CLI Client Usage
The clctl CLI tool invokes remote Lambda functions:
clctl --name <function-name> [cobra-args...]
Examples
# Simple invocation
clctl --name my-cli-app
# With flags
clctl --name my-cli-app --verbose --output json
# With subcommands
clctl --name my-cli-app deploy --environment prod
# Help
clctl --help
AWS Configuration
The CLI uses the AWS SDK for Go v2 and respects standard AWS configuration:
- AWS credentials from
~/.aws/credentialsor environment variables - Region from
AWS_REGIONenvironment variable or AWS config - IAM permissions required:
lambda:InvokeFunction
Local Development with cldebug
The cldebug cli allows you to test and debug your Lambda Cobra CLI applications locally without deploying to AWS. It starts your Lambda function as an RPC server and invokes it locally, simulating the Lambda runtime environment.
Installing cldebug
Install cldebug cli:
go install github.com/JayJamieson/cobra-lambda/cmd/cldebug@latest
Or build from source:
git clone https://github.com/JayJamieson/cobra-lambda.git
cd cobra-lambda
go build -o cldebug cmd/cldebug/main.go
Usage Examples
1. Invoke a compiled Lambda binary
First, build your Lambda function:
go build -o bootstrap ./iac/go-demo/main.go
Then invoke it locally with cldebug:
cldebug ./bootstrap arg1 arg2 --flag value
2. Invoke a Go file directly with go run
For rapid development, you can run your Lambda source file directly without building:
cldebug --go-run ./iac/go-demo/main.go arg1 arg2 --flag value
3. Debug mode
Enable debug logging to see what's happening under the hood:
cldebug --debug ./bootstrap arg1 arg2
This will show:
- Lambda process startup
- RPC connection details
- Invocation payload
- Process cleanup steps
Full Example
Using the example from iac/go-demo/:
# Build the Lambda function
cd iac/go-demo
go build -o bootstrap main.go
# Test locally with cldebug
cldebug ./bootstrap hello world
# Or use go run for faster iteration
cldebug --go-run main.go hello world
# With debug output
cldebug --debug ./bootstrap hello world
How It Works
-
Client Side: The
clctlCLI tool extracts the--nameflag to identify the target Lambda function, then forwards all remaining arguments as a JSON array payload. -
Lambda Side: The wrapper package:
- Intercepts
os.Stdoutandos.Stderrusing pipes - Redirects Cobra's output streams to a shared buffer
- Unmarshals the JSON array of arguments
- Executes the Cobra command with the provided arguments
- Returns all captured output as structured data
- Intercepts
-
Response: The client displays the returned output, making the remote execution feel like a local CLI invocation.
Examples
See the iac/ directory for complete examples:
iac/go-demo/- Go-based Cobra CLI wrapped for Lambdaiac/node-demo/- Node.js implementation showing the same patterniac/main.tf- Terraform configuration for deploying Lambda functions
License
MIT