Skip to content

Commit b402fce

Browse files
authored
Devenv Codegen (#2221)
devenv codegen
1 parent 0e0ea8c commit b402fce

File tree

13 files changed

+3722
-20
lines changed

13 files changed

+3722
-20
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Framework Code Generation
2+
on:
3+
push:
4+
5+
jobs:
6+
test:
7+
defaults:
8+
run:
9+
working-directory: framework/
10+
env:
11+
LOKI_TENANT_ID: promtail
12+
LOKI_URL: http://localhost:3030/loki/api/v1/push
13+
runs-on: ubuntu-latest
14+
permissions:
15+
id-token: write
16+
contents: read
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
test:
21+
- name: TestSmokeGenerate
22+
count: 1
23+
timeout: 10m
24+
name: ${{ matrix.test.name }}
25+
steps:
26+
- name: Checkout repo
27+
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
28+
- name: Configure AWS credentials using OIDC
29+
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
30+
with:
31+
role-to-assume: ${{ secrets.PUBLIC_AWS_ECR_ROLE }}
32+
aws-region: us-east-1
33+
- name: Authenticate to ECR Public
34+
id: login-ecr-public
35+
uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1
36+
with:
37+
registry-type: public
38+
- name: Set up Go
39+
uses: actions/setup-go@v5
40+
with:
41+
go-version-file: framework/go.mod
42+
- name: Cache Go modules
43+
uses: actions/cache@v4
44+
with:
45+
path: |
46+
~/.cache/go-build
47+
~/go/pkg/mod
48+
key: go-modules-${{ hashFiles('framework/go.sum') }}-${{ runner.os }}-framework-codegen
49+
restore-keys: |
50+
go-modules-${{ runner.os }}-framework-codegen
51+
go-modules-${{ runner.os }}
52+
- name: Install dependencies
53+
run: go mod download
54+
- name: Run Codegen Tests
55+
run: |
56+
go test -timeout ${{ matrix.test.timeout }} -v -count ${{ matrix.test.count }} -run ${{ matrix.test.name }}
57+
- name: Upload Logs
58+
if: always()
59+
uses: actions/upload-artifact@v4
60+
with:
61+
name: container-logs-${{ matrix.test.name }}
62+
path: framework/logs
63+
retention-days: 1

book/src/SUMMARY.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@
44
- [Framework](./framework/overview.md)
55
- [Basic Usage](./framework/getting_started.md)
66
- [Getting Started](./framework/getting_started.md)
7-
- [Your First Test](./framework/first_test.md)
8-
- [NodeSet Environment](./framework/nodeset_environment.md)
9-
- [NodeSet (Capabilities)](./framework/nodeset_capabilities.md)
10-
- [NodeSet (Local Docker builds)](./framework/nodeset_docker_rebuild.md)
7+
- [Generating Developer Environment](./framework/generate.md)
118
- [Advanced Usage](./framework/configuration.md)
129
- [CLI](./framework/cli.md)
1310
- [Configuration](./framework/configuration.md)
1411
- [Debugging Tests](framework/components/debug.md)
15-
- [Generating Tests](framework/generate.md)
1612
- [Creating your own components](./developing/developing_components.md)
1713
- [Exposing Components](framework/components/state.md)
1814
- [Asserting Logs](./developing/asserting_logs.md)

book/src/framework/generate.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
1-
# Generating Tests
1+
# Generating Chainlink Environment
22

3-
Some types of tests may require a lot of boilerplate and configurability, for example load and chaos tests.
3+
Our code generation tools automatically build complete Chainlink environments and test templates, which minimizes documentation and provides a framework that is both structured and easily extensible.
44

5-
We provide code generation tool that help you to start with best practes in such cases.
5+
Let's read `help` first and then build an environment for a single EVM network:
6+
```bash
7+
ctf gen -h
8+
# generate a new Chainlink environment in "devenv" directory with 4 Chainlink nodes and one EVM network. Generate CLI called "pcli" and enter the shell
9+
ctf gen env --cli pcli --product-name MyProduct --output-dir devenv --nodes 4
10+
```
611

7-
All generators have `-h` or `--help` flag, please read the docs!
12+
Follow further instructions in `devenv/README.md`
13+
14+
# Generating Infrastructure Testing Template
15+
16+
If you have Chainlink infrastructure already deployed it's useful to generate a workload + chaos suite template.
17+
```bash
18+
ctf gen load -h
19+
# generate test suite named ChaosGen, with workload + default chaos experiments (fail + latency) for all the pods that have app.kubernetes.io/instance annotation
20+
ctf gen load -w -n ChaosGen default
21+
```

framework/.changeset/v0.12.0.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Codegeneration for Devenv

framework/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# dev environment generated by manually testing CLI
2+
cmd/devenv/
3+
# dev environments generated by automated tests
4+
test-env*/

framework/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
Modular and data-driven harness for Chainlink on-chain and off-chain components.
44

5-
[![Documentation](https://img.shields.io/badge/Documentation-MDBook-blue?style=for-the-badge)](https://smartcontractkit.github.io/chainlink-testing-framework/framework/overview.html)
5+
[![Documentation](https://img.shields.io/badge/Documentation-MDBook-blue?style=for-the-badge)](https://smartcontractkit.github.io/chainlink-testing-framework/framework/overview.html)

framework/cmd/main.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"fmt"
55
"log"
66
"os"
7+
"os/exec"
78
"path/filepath"
9+
"runtime"
810
"strings"
911

1012
"github.com/pelletier/go-toml"
@@ -25,6 +27,119 @@ func main() {
2527
Aliases: []string{"g"},
2628
Usage: "Generates various test templates",
2729
Subcommands: []*cli.Command{
30+
{
31+
Name: "env",
32+
Aliases: []string{"e"},
33+
Usage: "Generate a Chainlink Node developer environment",
34+
Description: `🔗 Chainlink's Developer Environment Generator 🔗
35+
36+
Prerequisites:
37+
Just will be automatically installed if not available (via Homebrew on macOS).
38+
For other platforms, please install it manually: https://github.com/casey/just
39+
40+
Usage:
41+
42+
⚙️ Generate basic environment:
43+
ctf gen env --cli myenv --output-dir devenv --product-name Knilniahc --nodes 4
44+
45+
📜 Read the docs in devenv/README.md
46+
47+
🔧 Address all TODO comments and customize it
48+
`,
49+
ArgsUsage: "",
50+
Flags: []cli.Flag{
51+
&cli.StringFlag{
52+
Name: "cli",
53+
Aliases: []string{"c"},
54+
Usage: "Your devenv CLI binary name",
55+
},
56+
&cli.StringFlag{
57+
Name: "output-dir",
58+
Aliases: []string{"o"},
59+
Value: "devenv",
60+
Usage: "Your devenv directory",
61+
},
62+
&cli.StringFlag{
63+
Name: "product-name",
64+
Aliases: []string{"r"},
65+
Usage: "Your product name",
66+
},
67+
&cli.StringFlag{
68+
Name: "product-configuration-type",
69+
Aliases: []string{"p"},
70+
Value: "evm-single",
71+
Usage: "Product configuration type/layout (single network, multi-network, etc)",
72+
},
73+
&cli.IntFlag{
74+
Name: "nodes",
75+
Aliases: []string{"n"},
76+
Value: 4,
77+
Usage: "Chainlink Nodes",
78+
},
79+
},
80+
Action: func(c *cli.Context) error {
81+
outputDir := c.String("output-dir")
82+
productConfType := c.String("product-configuration-type")
83+
nodes := c.Int("nodes")
84+
cliName := c.String("cli")
85+
if cliName == "" {
86+
return fmt.Errorf("CLI name can't be empty, choose your CLI name")
87+
}
88+
productName := c.String("product-name")
89+
if productName == "" {
90+
return fmt.Errorf("Product name must be specified, call your product somehow, any name")
91+
}
92+
framework.L.Info().
93+
Str("OutputDir", outputDir).
94+
Str("Name", cliName).
95+
Int("CLNodes", nodes).
96+
Str("ProductConfigurationType", productConfType).
97+
Msg("Generating developer environment")
98+
99+
cg, err := framework.NewEnvBuilder(cliName, nodes, productConfType, productName).
100+
OutputDir(outputDir).
101+
Build()
102+
if err != nil {
103+
return fmt.Errorf("failed to create codegen: %w", err)
104+
}
105+
if err := cg.Write(); err != nil {
106+
return fmt.Errorf("failed to generate module: %w", err)
107+
}
108+
109+
fmt.Println()
110+
fmt.Printf("📁 Your environment directory is: %s\n", outputDir)
111+
fmt.Printf("💻 Your CLI name is: %s\n", cliName)
112+
fmt.Printf("📜 More docs can be found in %s/README.md\n", outputDir)
113+
fmt.Printf("⬛ Entering the shell..\n")
114+
fmt.Println()
115+
116+
// Ensure 'just' is installed before proceeding
117+
if err := ensureJustInstalled(); err != nil {
118+
return fmt.Errorf("failed to ensure 'just' is installed: %w", err)
119+
}
120+
121+
cmd := exec.Command("just", "cli")
122+
cmd.Env = os.Environ()
123+
cmd.Dir = outputDir
124+
out, err := cmd.CombinedOutput()
125+
if err != nil {
126+
return fmt.Errorf("failed to build CLI via Justfile: %w, output: %s", err, string(out))
127+
}
128+
if err := os.Chdir(outputDir); err != nil {
129+
return err
130+
}
131+
cmd = exec.Command(cliName, "sh")
132+
cmd.Env = os.Environ()
133+
cmd.Stdin = os.Stdin
134+
cmd.Stdout = os.Stdout
135+
cmd.Stderr = os.Stderr
136+
err = cmd.Run()
137+
if err != nil {
138+
return fmt.Errorf("failed to enter devenv shell: %w", err)
139+
}
140+
return nil
141+
},
142+
},
28143
{
29144
Name: "load",
30145
Aliases: []string{"l"},
@@ -359,3 +474,36 @@ func RemoveCacheFiles() error {
359474
framework.L.Info().Msg("All cache files has been removed")
360475
return nil
361476
}
477+
478+
// ensureJustInstalled checks if 'just' is available in PATH, and if not, attempts to install it.
479+
// On macOS, it tries to install via Homebrew. On other platforms, it provides installation instructions.
480+
func ensureJustInstalled() error {
481+
// Check if just is already available
482+
if _, err := exec.LookPath("just"); err == nil {
483+
return nil
484+
}
485+
486+
fmt.Println("⚠️ 'just' command not found in PATH")
487+
fmt.Println("📦 Attempting to install 'just'...")
488+
489+
// Try to install via Homebrew on macOS
490+
if runtime.GOOS == "darwin" {
491+
// Check if Homebrew is available
492+
if _, err := exec.LookPath("brew"); err == nil {
493+
fmt.Println("🍺 Installing 'just' via Homebrew...")
494+
cmd := exec.Command("brew", "install", "just")
495+
cmd.Stdout = os.Stdout
496+
cmd.Stderr = os.Stderr
497+
if err := cmd.Run(); err != nil {
498+
return fmt.Errorf("failed to install 'just' via Homebrew: %w. Please install manually: brew install just", err)
499+
}
500+
fmt.Println("✅ Successfully installed 'just'")
501+
return nil
502+
}
503+
// Homebrew not available, provide instructions
504+
return fmt.Errorf("'just' is not installed and Homebrew is not available. Please install 'just' manually:\n brew install just\n Or visit: https://github.com/casey/just")
505+
}
506+
507+
// For non-macOS platforms, provide installation instructions
508+
return fmt.Errorf("'just' is not installed. Please install it manually:\n Visit: https://github.com/casey/just\n Or use your package manager (e.g., apt install just, pacman -S just)")
509+
}

0 commit comments

Comments
 (0)