Skip to content

feat: promote envprov to a loadable provisioner type#489

Open
Siddhartha-singh01 wants to merge 2 commits into
score-spec:mainfrom
Siddhartha-singh01:feat/envprov-as-loadable-provisioner
Open

feat: promote envprov to a loadable provisioner type#489
Siddhartha-singh01 wants to merge 2 commits into
score-spec:mainfrom
Siddhartha-singh01:feat/envprov-as-loadable-provisioner

Conversation

@Siddhartha-singh01
Copy link
Copy Markdown
Contributor

Promotes the built-in envprov provisioner to a first-class, loadable provisioner type alongside templateprov and cmdprov.

Resolves #488

Description

The envprov.Provisioner was previously hardcoded in generate.go and always resolved environment variables via os.LookupEnv with no way for developers to override or configure it. As discussed with @chris-stephenson in #488, the cleanest path is to make envprov loadable through the existing provisioners YAML mechanism using a local-env:// URI scheme.

Changes:

  • internal/provisioners/envprov/envprov.go: Added YAML struct fields and a Parse() function following the same pattern as cmdprov.Parse() and templateprov.Parse(). The provisioner works in two modes: legacy (created via new(Provisioner) with original behavior preserved) and YAML-loaded (created via Parse() with standard type/class matching).

  • internal/provisioners/loader/load.go: Added local-env case to the URI scheme switch so the loader can parse and load environment provisioners from .provisioners.yaml files.

  • internal/command/default.provisioners.yaml: Added a default local-env://default-provisioners/environment entry so new projects get the environment provisioner out of the box via score-compose init.

  • internal/command/generate.go: Replaced the hardcoded new(envprov.Provisioner) with logic that searches loaded provisioners for an *envprov.Provisioner first, and falls back to creating one if not found (backward compatibility for existing projects).

What does this PR do?

Allows the environment provisioner to be loaded from .provisioners.yaml files like any other provisioner, instead of being hardcoded. Developers can now override or replace the default environment provisioner by defining their own local-env:// entry in a higher-priority provisioners file.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • New chore (expected functionality to be implemented)

Checklist:

  • My change requires a change to the documentation.
    • I have updated the documentation accordingly.
  • I've signed off with an email address that matches the commit author.

@Siddhartha-singh01 Siddhartha-singh01 force-pushed the feat/envprov-as-loadable-provisioner branch from 9abffaa to b4b2d0b Compare May 16, 2026 16:43
Copy link
Copy Markdown
Contributor

@chris-stephenson chris-stephenson left a comment

Choose a reason for hiding this comment

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

Not sure the fallback logic is correct.

Comment thread internal/command/generate.go Outdated
// or create a fallback for backward compatibility with projects initialized before this entry existed.
var environmentProvisioner *envprov.Provisioner
for _, p := range loadedProvisioners {
if ep, ok := p.(*envprov.Provisioner); ok {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this is the wrong check. You shoudl check for Type. The point here is that we want people to provide their own provisioners for environment - not force them to use the envprov implementation.

Add local-env:// URI scheme support so environment provisioners can be
loaded from .provisioners.yaml files alongside template:// and cmd://.

The envprov.Provisioner now supports a Parse() function for YAML loading,
while preserving backward compatibility for projects that don't have
the local-env:// entry in their provisioners yet.

Resolves score-spec#488

Signed-off-by: Siddhartha Singh <siddharthagithub0007@gmail.com>
The fallback in generate.go identified the environment provisioner via a
Go type assertion (*envprov.Provisioner), which ignored any user-supplied
provisioner declared with type: environment and force-appended the
built-in implementation regardless.

Match on Provisioner.Type() == "environment" instead, so a cmd:// or
template:// provisioner that handles the environment type is used as-is
and the built-in envprov fallback is only appended when no provisioner
claims the type. The concrete *envprov.Provisioner is still captured
non-fatally for Accessed(), used to write the env file; that write is
now guarded for the nil case when a custom provisioner is supplied.

Signed-off-by: Siddhartha Singh <siddharthagithub0007@gmail.com>
@Siddhartha-singh01 Siddhartha-singh01 force-pushed the feat/envprov-as-loadable-provisioner branch from d53b399 to 74707df Compare May 18, 2026 18:52
@Siddhartha-singh01
Copy link
Copy Markdown
Contributor Author

Thanks for the catch @chris-stephenson, you're right.

I spent some time going back through the provisioner loading path again to make sure I understood how it all connects the loader, LoadProvisionersFromDirectory, and how ProvisionResources picks a provisioner by Match(). Once I looked at it properly, your point made sense: the old p.(*envprov.Provisioner) assertion was only checking "is this our built-in struct", which isn't the same question as "does this handle environment resources". So if someone supplied their own cmd:// or template:// provisioner with type: environment, it would get ignored and we'd force the built-in envprov on top anyway exactly what we don't want.

I've changed the check to match on p.Type() == "environment" instead. Now the built-in envprov fallback is only appended when no provisioner already claims the environment type, so a user-supplied one is used as-is.

One thing worth flagging: I kept the *envprov.Provisioner type assertion in one spot, but only as a non-fatal capture Accessed() (used to write the -env-file) is specific to the built-in implementation and isn't part of the Provisioner interface. When a custom provisioner handles environment, that reference is just nil, so I guarded the env-file write accordingly.

Build, go vet and the env/generate tests pass locally.

Could you take another look when you get a chance

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.

Allow explicit provisioner selection for environment resources

2 participants