manifest
One committed manifest per build shape, and the profile picks. manifest.json is what ships; manifest.test.json extends it and adds the beta modules the gametest build needs. extends works the way tsconfig.json does.
Install
regolith install manifest
It runs first, before anything reads the manifest:
{
"regolith": {
"profiles": {
"default": {
"filters": [{ "filter": "manifest" }, { "filter": "bundler" }]
},
"test": {
"filters": [
{ "filter": "manifest", "settings": { "manifestPath": "BP/manifest.test.json" } },
{ "filter": "bundler", "settings": { "tsConfigPath": "tsconfig.test.json" } }
]
}
}
}
}
The profile picks a manifest the same way it picks an entry point. Nothing else changes.
Authoring
packs/BP/manifest.json what ships: stable modules only
packs/BP/manifest.test.json extends it, adds @minecraft/server-gametest
{
"extends": "./manifest.json",
"header": { "name": "DEV pack" },
"dependencies": [
{ "uuid": "5e0e2a5b-74e2-4dd6-9c11-8a4f3f6b2d90", "version": "0.1.0" },
{ "module_name": "@minecraft/server", "version": "2.9.0-beta" },
{ "module_name": "@minecraft/server-gametest", "version": "1.0.0-beta" }
]
}
The base stays release truth and variants add to it, so a filter that never ran, or ran wrong, gives you a broken test build — never a release with beta modules in it.
Merge rules are TypeScript's:
- Objects merge key by key, recursively.
header: { name }overrides the name and keeps the uuid. - Arrays and scalars replace outright.
dependenciesin the child becomes the whole list, so restating it is how a variant adds, re-versions or drops an entry. extendsis a relative path resolved against the file that declares it, and chains as deep as you like:manifest.test.jsonextendsmanifest.dev.json, which extendsmanifest.json.
More than one pack
A build shape that changes both manifests names both, in one array:
{
"filter": "manifest",
"settings": { "manifestPath": ["BP/manifest.test.json", "RP/manifest.test.json"] }
}
Each entry resolves independently and is written as manifest.json beside its own source. Variant filenames need not match across packs. To vary only one pack, name only that one; a manifest with no extends is a no-op to resolve.
Do not list the filter twice in a profile. Every run sweeps the variants in BP/ and RP/, so the first run deletes what the second one was going to read. One entry, one array.
What it generates
| Output | Where | Commit it? |
|---|---|---|
manifest.json | beside each resolved source, in the temp workspace | no — the committed file is the base |
After resolving, every manifest.*.json left in BP/ and RP/ is deleted from the temp workspace, both pack roots, whichever ones the profile named. manifest.json itself and files that merely look close (manifest.json.bak) are left alone. Nothing is written until every entry has resolved and validated, so a broken extends chain leaves the workspace as it was.
Validation
The resolved manifest is checked before anything is written, so a broken merge fails the build instead of shipping. Every check names the failing path.
| Check | Fails when |
|---|---|
format_version | Not the number 3 |
header | Missing, or its uuid is missing |
header.version | Present but not a SemVer string |
header.min_engine_version | Present but not a SemVer string |
header.base_game_version | Present but not a SemVer string |
modules[].version | Present but not a SemVer string, for any module |
dependencies[].version | Present but not a SemVer string, for any dependency |
metadata.authors | Missing, empty, or containing a value that is not a string |
A version 3 manifest takes a SemVer string everywhere a version appears — "1.0.0", never the [major, minor, patch] array form.
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
manifestPath | string | string[] | "BP/manifest.json" | Manifest to resolve. An array resolves one per pack |
pretty | false | { indent?, size? } | false | How generated JSON is laid out. Absent or false writes it minified. An object lays it out: indent is "tab" or "space", size the characters per level (2 for spaces, 1 for tabs when omitted) |
Paths are relative to Regolith's temp workspace; a leading packs/ is stripped, so packs/BP/manifest.test.json and BP/manifest.test.json both work. The result is always written as manifest.json beside its source — Bedrock accepts no other name.