Skip to content

Env groups belong in rbxplace.toml, not in each tool's TOML #5

Description

@dev-bap

Filed first as "rbxmeta overlays only target one env". That was the symptom, and fixing it inside rbxmeta.toml would have been the wrong shape. The question underneath is where an env group lives at all.

The real observation

Three files have independently grown a way to talk about several envs, and no two agree.

Where Spelling Scope
rbxplace.toml --env all every env, hardcoded, no way to name a subset
rbxapikey.toml [keys.deploy.envs] with named groups (ci = ["dev","staging"]) that key only
rbxmeta.toml, rbxshop.toml, rbxconfig.toml [envs.<name>], exactly one env each that file only

A project with dev, staging, qa and prod writes the non-production set out by hand in rbxapikey.toml as ci, again as three [envs.*] blocks in rbxmeta.toml, again as three in rbxshop.toml, and cannot express it at all for --env. Four spellings of one fact, and adding qa2 means finding all four.

rbxplace.toml is the one file that defines what an env is — every tool resolves --env through rbx_core's resolver against it. A grouping of envs is a fact about envs. It belongs there, and then it is available everywhere at once instead of being re-invented per tool.

The shape

[owner]
type = "group"
id = 1234567

[groups]                      # third reserved table, beside [owner] and [codegen]
nonprod = ["dev", "staging", "qa"]
live    = ["prod"]

[dev]
universe_id = 1
[staging]
universe_id = 2
[qa]
universe_id = 3
[prod]
universe_id = 4

--env nonprod then means those three envs, in every command that accepts --env, resolved by the shared resolver. --env all becomes the special case of a group nobody has to declare, rather than the only grouping that exists.

What falls out for free

  • rbx shop sync --env nonprod, rbx check --env nonprod, rbx place upload --env nonprod — every command that already fans out over --env all fans out over a named subset with no per-command work.
  • [envs.nonprod] in rbxmeta.toml and rbxshop.toml resolves through the same table, so the original complaint is answered without those files learning anything about groups. Layering becomes base, then group overlay, then the env's own overlay, most specific winning.
  • rbxapikey.toml's named groups collapse into it. [keys.deploy.envs] ci = ["dev","staging"] becomes envs = ["nonprod"] or keeps its inline form as sugar. The fan-out naming (deploy_ci) can key off the group name either way. This is the one place a group already exists, so it is also the migration case that has to keep working.

What has to be decided, and none of it is obvious

Where a group is refused. Live-ops commands reject --env all on the grounds that each env is a different experience and a glob must not reach production. A group is a smaller glob and the argument is identical: single_universe() and resolve_single_env() must reject a group with the same error, not silently take the first. rbx open likewise.

Whether a group can contain a group. Nesting invites cycles and buys very little. Flat, with a load-time error on any name that is not an env, is probably right.

Name collisions. A group named the same as an env, or named all, owner, codegen. All must be errors at load, naming both sides, the way two envs resolving to one env name already are.

Ordering. --env all walks envs in name order today. A group is a list, so it has an author-chosen order. Whether fan-out follows the declared order or sorts anyway matters for rbx shop sync --env nonprod, which holds one lockfile across the whole walk.

Codegen. A group is not an env and must not appear in EnvironmentType. rbx env gen-module reads rbxplace.toml; [groups] has to be a reserved table it skips, like [codegen], and rbx env list needs to show groups without pretending they are envs.

Whether an overlay may target a group and the group may be empty. [envs.nonprod] with nonprod = [] should be an error, on the same reasoning rbxapikey.toml already refuses an empty env group: a declaration targeting nothing is never what was meant.

Pull. The hard half, and the reason to design before building. rbx meta pull --env dev writes back what diverges from base. With groups it also has to notice that the value it is about to write into [envs.dev] already sits in [envs.nonprod], and leave it alone — otherwise every pull churns out redundant overlays, which is exactly what the differential-pull rules exist to prevent. That logic is per-tool and cannot be shared, so it is three implementations of one rule and the place this will actually go wrong.

What this replaces

Both of the shapes floated earlier are now clearly worse:

  • applies_to = [...] inside each overlay block — self-describing, but the group's member list is repeated in every file that uses it, which is the problem restated.
  • rbxmeta.dev.toml extending a base file — makes each file smaller and leaves a value shared by three envs written three times, plus a new question about which file wins.

Sequence

rbxplace.toml and rbx_core's resolver first, with rbx env list showing groups and every single-env command refusing one. That alone delivers --env <group> fan-out across the tools that already do --env all, and is testable offline. The overlay resolution in meta, shop and config is a second step, and pull is a third — it is the part worth prototyping against a real multi-env project before committing to it.


Re-filed from #119 when the repository history was reset for 0.1.0. Issue and PR numbers referenced above belong to the previous numbering and no longer resolve; the reasoning does not depend on them.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions