Skip to content

refactor(registry): load runtime and service versions from JSON#260

Open
pjcdawkins wants to merge 2 commits intomainfrom
refactor/abstract-fs
Open

refactor(registry): load runtime and service versions from JSON#260
pjcdawkins wants to merge 2 commits intomainfrom
refactor/abstract-fs

Conversation

@pjcdawkins
Copy link
Contributor

Summary

Replace hardcoded string constants for Runtime and ServiceName types with struct types loaded from an embedded registry.json file at init time.

This is the first in a series of PRs incorporating changes from @akalipetis's feature/abstract-fs branch, cleaned up and split into reviewable pieces. Subsequent PRs will add fs.FS abstraction, a discovery package, and non-interactive mode.

Author: @akalipetis

🤖 Generated with Claude Code

Replace hardcoded string constants for Runtime and ServiceName types
with struct types loaded from an embedded registry.json file at init
time. This makes it possible to update supported versions without
code changes.

- Add registry.go and registry.json (embedded JSON data for all
  runtimes and services with their supported versions)
- Rewrite Runtime from string const to struct with Name, Type,
  Versions, Docs fields
- Rewrite ServiceName from string const to struct with Name, Type,
  Versions, Disk, Docs fields
- Update RuntimeForStack to look up runtimes by type string
- Update all callers to use runtime.Type string comparisons instead
  of const equality
- Remove version.go and generate_versions.go (superseded by registry)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors the question/models layer to source runtime and service metadata (including supported versions) from an embedded registry.json instead of hardcoded Go constants/maps, and updates the interactive questionnaire flow to use the new struct-based runtime/service representations.

Changes:

  • Introduces embedded registry.json plus init-time loading into global models.Runtimes and models.ServiceNames.
  • Replaces hardcoded Runtime/ServiceName constants and version maps with Runtime / ServiceName structs and helper lookup/default-version methods.
  • Updates question flow and tests to compare by runtime.Type string and to pull versions from the registry.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
internal/question/web_command.go Updates PHP runtime check to use answers.Type.Runtime.Type.
internal/question/type.go Adapts stack→runtime detection to pointer-based runtime and uses DefaultVersion().
internal/question/socket_family.go Switches runtime comparisons to answers.Type.Runtime.Type.
internal/question/services.go Sources service supported versions from registry-backed ServiceName.Versions.Supported.
internal/question/models/version.go Removes hardcoded language/service version maps and default-version helper.
internal/question/models/stack.go Changes RuntimeForStack() to return a registry-backed *Runtime (or nil).
internal/question/models/service_name.go Replaces service constants with a registry-backed ServiceName struct + helpers.
internal/question/models/runtime.go Replaces runtime constants with a registry-backed Runtime struct + helpers.
internal/question/models/registry.json Adds embedded upstream registry data for runtimes/services and versions.
internal/question/models/registry.go Loads and partitions registry into Runtimes and ServiceNames at init time.
internal/question/models/generate_versions.go Removes codegen tool previously used to generate version maps.
internal/question/models/answer.go Updates RuntimeType to hold *Runtime and adjusts String/JSON behavior.
internal/question/locations.go Updates PHP runtime check to use answers.Type.Runtime.Type.
internal/question/build_steps_test.go Updates tests to resolve runtimes from models.Runtimes via type.
internal/question/build_steps.go Updates Node.js runtime check to use answers.Type.Runtime.Type.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 44 to 48
runtime := models.RuntimeForStack(answers.Stack)
if runtime == "" {
if runtime == nil {
question := &survey.Select{
Message: "What language is your project using? We support the following:",
Options: models.Runtimes.AllTitles(),
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Runtime is now a pointer, but the defer above prints answers.Type.Runtime.Title() when answers.Stack != GenericStack. If survey.AskOne() or RuntimeByTitle() returns an error, the function returns before answers.Type.Runtime is assigned, and the deferred print will panic on nil. Add a nil check in the defer (or only print after answers.Type.Runtime is set and the function is returning nil).

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +35
func init() {
allRuntimes := map[string]*Runtime{}
if err := json.Unmarshal(registry, &allRuntimes); err != nil {
log.Fatal(err)
}
for _, r := range allRuntimes {
if r.Runtime {
Runtimes = append(Runtimes, r)
}
}

allServices := map[string]*ServiceName{}
if err := json.Unmarshal(registry, &allServices); err != nil {
log.Fatal(err)
}
for _, s := range allServices {
if !s.Runtime {
ServiceNames = append(ServiceNames, s)
}
}
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

registry.json is unmarshaled into maps and then appended into slices via map iteration, which produces nondeterministic ordering. Because Runtimes.AllTitles() / ServiceNames.AllTitles() preserve slice order, the prompt option ordering will vary between runs and can make tests/UX flaky. Sort Runtimes and ServiceNames (e.g., by Type or Name) after loading; also consider unmarshaling once and partitioning rather than unmarshaling the same bytes twice.

Copilot uses AI. Check for mistakes.
Comment on lines +13 to 20
URL string
Web struct {
Commands struct {
Start string
}
}
Locations map[string]map[string]any
}
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Docs struct layout doesn’t match the JSON structure: in registry.json, locations is under docs.web.locations, but here Locations is a sibling of Web. As a result, Locations will never unmarshal and will always be nil. Move Locations under Web (and add the appropriate json tags) to actually load the embedded docs data.

Suggested change
URL string
Web struct {
Commands struct {
Start string
}
}
Locations map[string]map[string]any
}
URL string `json:"url"`
Web struct {
Commands struct {
Start string `json:"start"`
} `json:"commands"`
Locations map[string]map[string]any `json:"locations"`
} `json:"web"`
} `json:"docs"`

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +22
Docs struct {
Relationship string
URL string
}
)
Endpoint string
MinDiskSize *int
Versions struct {
Supported []string
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several fields won’t unmarshal from registry.json because the JSON keys are snake_case (e.g., relationship_name, min_disk_size) but the struct has no json tags. That means Docs.Relationship and MinDiskSize will remain empty/nil even though the registry provides them. Add explicit json tags (and ensure field names match the upstream schema) so the registry-backed struct fields are actually populated.

Copilot uses AI. Check for mistakes.
- type.go: add nil check for Runtime in defer to prevent panic when
  RuntimeByTitle returns an error before Runtime is assigned
- registry.go: sort Runtimes and ServiceNames after loading from the
  map to ensure deterministic ordering
- runtime.go: move Locations field inside Web struct to match the JSON
  nesting (docs.web.locations)
- service_name.go: add json tags for relationship_name and
  min_disk_size so they unmarshal correctly from snake_case JSON keys

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants