Skip to content

Commit 3442ad2

Browse files
Cre init improvements (#315)
* updated metadata to support new field, category workflow is now the only installable template with the CLI * updated cre init to support multiple workflow dir for bun install instruction * fixed RPC url replacement issue on cre init * cre init wizard only show template selected card when passing --template param * fixed test/linter * updated error message when cre init --template is not found --------- Co-authored-by: Tarcísio Zotelli Ferraz <tar-2008.ferraz@hotmail.com>
1 parent e62a9ea commit 3442ad2

6 files changed

Lines changed: 243 additions & 71 deletions

File tree

cmd/creinit/creinit.go

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -196,22 +196,30 @@ func (h *handler) Execute(inputs Inputs) error {
196196
return fmt.Errorf("failed to fetch templates: %w", err)
197197
}
198198

199+
// Filter to only workflow templates (category == "workflow")
200+
var workflowTemplates []templaterepo.TemplateSummary
201+
for _, t := range templates {
202+
if t.Category == templaterepo.CategoryWorkflow {
203+
workflowTemplates = append(workflowTemplates, t)
204+
}
205+
}
206+
199207
// Resolve template from flag if provided
200208
var selectedTemplate *templaterepo.TemplateSummary
201209
if inputs.TemplateName != "" {
202-
for i := range templates {
203-
if templates[i].Name == inputs.TemplateName {
204-
selectedTemplate = &templates[i]
210+
for i := range workflowTemplates {
211+
if workflowTemplates[i].Name == inputs.TemplateName || workflowTemplates[i].ID == inputs.TemplateName {
212+
selectedTemplate = &workflowTemplates[i]
205213
break
206214
}
207215
}
208216
if selectedTemplate == nil {
209-
return fmt.Errorf("template %q not found", inputs.TemplateName)
217+
return fmt.Errorf("template %q not found. Run 'cre templates list' to see all available templates", inputs.TemplateName)
210218
}
211219
}
212220

213221
// Run the interactive wizard
214-
result, err := RunWizard(inputs, isNewProject, startDir, templates, selectedTemplate)
222+
result, err := RunWizard(inputs, isNewProject, startDir, workflowTemplates, selectedTemplate)
215223
if err != nil {
216224
return fmt.Errorf("wizard error: %w", err)
217225
}
@@ -287,22 +295,24 @@ func (h *handler) Execute(inputs Inputs) error {
287295
return fmt.Errorf("failed to scaffold template: %w", err)
288296
}
289297

298+
// Patch RPC URLs into project.yaml for all templates (including those with projectDir).
299+
// Templates that ship their own project.yaml still need user-provided RPCs applied.
300+
projectYAMLPath := filepath.Join(projectRoot, constants.DefaultProjectSettingsFileName)
301+
if isNewProject && h.pathExists(projectYAMLPath) {
302+
if err := settings.PatchProjectRPCs(projectYAMLPath, networkRPCs); err != nil {
303+
return fmt.Errorf("failed to update RPC URLs in project.yaml: %w", err)
304+
}
305+
}
306+
290307
// Templates with projectDir provide their own project structure — skip config generation.
291308
// Only built-in templates (no projectDir) need config files generated by the CLI.
292309
if selectedTemplate.ProjectDir == "" {
293-
// Handle project.yaml
294-
projectYAMLPath := filepath.Join(projectRoot, constants.DefaultProjectSettingsFileName)
295-
if isNewProject {
296-
if h.pathExists(projectYAMLPath) {
297-
if err := settings.PatchProjectRPCs(projectYAMLPath, networkRPCs); err != nil {
298-
return fmt.Errorf("failed to update RPC URLs in project.yaml: %w", err)
299-
}
300-
} else {
301-
networks := selectedTemplate.Networks
302-
repl := settings.GetReplacementsWithNetworks(networks, networkRPCs)
303-
if e := settings.FindOrCreateProjectSettings(projectRoot, repl); e != nil {
304-
return e
305-
}
310+
// Generate project.yaml if the template didn't provide one
311+
if isNewProject && !h.pathExists(projectYAMLPath) {
312+
networks := selectedTemplate.Networks
313+
repl := settings.GetReplacementsWithNetworks(networks, networkRPCs)
314+
if e := settings.FindOrCreateProjectSettings(projectRoot, repl); e != nil {
315+
return e
306316
}
307317
}
308318

@@ -449,7 +459,14 @@ func (h *handler) printSuccessMessage(projectRoot string, tmpl *templaterepo.Tem
449459
sb.WriteString(ui.RenderStep("2. Install Bun (if needed):") + "\n")
450460
sb.WriteString(" " + ui.RenderDim("npm install -g bun") + "\n\n")
451461
sb.WriteString(ui.RenderStep("3. Install dependencies:") + "\n")
452-
sb.WriteString(" " + ui.RenderDim("bun install --cwd ./"+primaryWorkflow) + "\n\n")
462+
if isMultiWorkflow {
463+
for _, wf := range workflows {
464+
sb.WriteString(" " + ui.RenderDim("bun install --cwd ./"+wf.Dir) + "\n")
465+
}
466+
} else {
467+
sb.WriteString(" " + ui.RenderDim("bun install --cwd ./"+primaryWorkflow) + "\n")
468+
}
469+
sb.WriteString("\n")
453470

454471
if isMultiWorkflow {
455472
sb.WriteString(ui.RenderStep("4. Run a workflow:") + "\n")

cmd/creinit/creinit_test.go

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ var testGoTemplate = templaterepo.TemplateSummary{
125125
Title: "Test Go Template",
126126
Description: "A test Go template",
127127
Language: "go",
128-
Category: "test",
128+
Category: "workflow",
129129
Author: "Test",
130130
License: "MIT",
131131
Networks: []string{"ethereum-testnet-sepolia"},
@@ -146,7 +146,7 @@ var testTSTemplate = templaterepo.TemplateSummary{
146146
Title: "Test TypeScript Template",
147147
Description: "A test TypeScript template",
148148
Language: "typescript",
149-
Category: "test",
149+
Category: "workflow",
150150
Author: "Test",
151151
License: "MIT",
152152
Workflows: []templaterepo.WorkflowDirEntry{{Dir: "my-workflow"}},
@@ -166,7 +166,7 @@ var testStarterTemplate = templaterepo.TemplateSummary{
166166
Title: "Starter Go Template",
167167
Description: "A starter Go template",
168168
Language: "go",
169-
Category: "test",
169+
Category: "workflow",
170170
Author: "Test",
171171
License: "MIT",
172172
Workflows: []templaterepo.WorkflowDirEntry{{Dir: "my-workflow"}},
@@ -186,7 +186,7 @@ var testMultiNetworkTemplate = templaterepo.TemplateSummary{
186186
Title: "Test Multi-Chain Template",
187187
Description: "A template requiring multiple chains",
188188
Language: "go",
189-
Category: "test",
189+
Category: "workflow",
190190
Author: "Test",
191191
License: "MIT",
192192
Networks: []string{"ethereum-testnet-sepolia", "ethereum-mainnet"},
@@ -207,7 +207,7 @@ var testBuiltInGoTemplate = templaterepo.TemplateSummary{
207207
Title: "Hello World (Go)",
208208
Description: "A built-in Go template",
209209
Language: "go",
210-
Category: "getting-started",
210+
Category: "workflow",
211211
Author: "Test",
212212
License: "MIT",
213213
},
@@ -222,7 +222,7 @@ var testMultiWorkflowTemplate = templaterepo.TemplateSummary{
222222
Title: "Bring Your Own Data (Go)",
223223
Description: "Bring your own off-chain data on-chain with PoR and NAV publishing.",
224224
Language: "go",
225-
Category: "data-feeds",
225+
Category: "workflow",
226226
Author: "Test",
227227
License: "MIT",
228228
Networks: []string{"ethereum-testnet-sepolia"},
@@ -247,7 +247,7 @@ var testSingleWorkflowWithPostInit = templaterepo.TemplateSummary{
247247
Title: "KV Store (Go)",
248248
Description: "Read, increment, and write a counter in AWS S3.",
249249
Language: "go",
250-
Category: "off-chain-storage",
250+
Category: "workflow",
251251
Author: "Test",
252252
License: "MIT",
253253
Workflows: []templaterepo.WorkflowDirEntry{{Dir: "my-workflow"}},
@@ -261,6 +261,30 @@ var testSingleWorkflowWithPostInit = templaterepo.TemplateSummary{
261261
},
262262
}
263263

264+
var testProjectDirWithNetworks = templaterepo.TemplateSummary{
265+
TemplateMetadata: templaterepo.TemplateMetadata{
266+
Kind: "starter-template",
267+
Name: "starter-with-projectdir",
268+
Title: "Starter With ProjectDir",
269+
Description: "A starter template that ships its own project structure",
270+
Language: "typescript",
271+
Category: "workflow",
272+
Author: "Test",
273+
License: "MIT",
274+
ProjectDir: ".",
275+
Networks: []string{"ethereum-testnet-sepolia", "ethereum-mainnet"},
276+
Workflows: []templaterepo.WorkflowDirEntry{
277+
{Dir: "my-workflow", Description: "Test workflow"},
278+
},
279+
},
280+
Path: "starter-templates/test/starter-with-projectdir",
281+
Source: templaterepo.RepoSource{
282+
Owner: "test",
283+
Repo: "templates",
284+
Ref: "main",
285+
},
286+
}
287+
264288
func newMockRegistry() *mockRegistry {
265289
return &mockRegistry{
266290
templates: []templaterepo.TemplateSummary{
@@ -271,6 +295,7 @@ func newMockRegistry() *mockRegistry {
271295
testBuiltInGoTemplate,
272296
testMultiWorkflowTemplate,
273297
testSingleWorkflowWithPostInit,
298+
testProjectDirWithNetworks,
274299
},
275300
}
276301
}
@@ -570,6 +595,45 @@ func TestInitRemoteTemplateKeepsProjectYAML(t *testing.T) {
570595
require.Contains(t, string(envContent), "GITHUB_API_TOKEN=test-token")
571596
}
572597

598+
func TestInitProjectDirTemplateRpcPatching(t *testing.T) {
599+
sim := chainsim.NewSimulatedEnvironment(t)
600+
defer sim.Close()
601+
602+
tempDir := t.TempDir()
603+
restoreCwd, err := testutil.ChangeWorkingDirectory(tempDir)
604+
require.NoError(t, err)
605+
defer restoreCwd()
606+
607+
// Template with ProjectDir set AND Networks — the bug was that RPC URLs
608+
// were silently dropped because the patching was inside the ProjectDir=="" block.
609+
inputs := Inputs{
610+
ProjectName: "projectDirProj",
611+
TemplateName: "starter-with-projectdir",
612+
WorkflowName: "my-workflow",
613+
RpcURLs: map[string]string{
614+
"ethereum-testnet-sepolia": "https://sepolia.custom.com",
615+
"ethereum-mainnet": "https://mainnet.custom.com",
616+
},
617+
}
618+
619+
h := newHandlerWithRegistry(sim.NewRuntimeContext(), newMockRegistry())
620+
require.NoError(t, h.ValidateInputs(inputs))
621+
require.NoError(t, h.Execute(inputs))
622+
623+
projectRoot := filepath.Join(tempDir, "projectDirProj")
624+
projectYAML, err := os.ReadFile(filepath.Join(projectRoot, constants.DefaultProjectSettingsFileName))
625+
require.NoError(t, err)
626+
content := string(projectYAML)
627+
628+
// User-provided RPCs must be patched even though ProjectDir is set
629+
require.Contains(t, content, "https://sepolia.custom.com",
630+
"user RPC URL for sepolia should be patched into project.yaml for templates with ProjectDir")
631+
require.Contains(t, content, "https://mainnet.custom.com",
632+
"user RPC URL for mainnet should be patched into project.yaml for templates with ProjectDir")
633+
require.NotContains(t, content, "https://default-rpc.example.com",
634+
"mock default URLs should be replaced by user-provided URLs")
635+
}
636+
573637
func TestTemplateNotFound(t *testing.T) {
574638
sim := chainsim.NewSimulatedEnvironment(t)
575639
defer sim.Close()

0 commit comments

Comments
 (0)