§ Reference · Commands
pravo init
Install the providers a root needs, write the lock file and generate the CUE packages that a specification imports.
On this page
Quick reference
| Synopsis | pravo init [--upgrade] [-t key=value] [-T] |
| Run it | Once per root, after a checkout, and after a provider constraint changes. |
| Network | Needed to resolve versions. Not needed when pravo.lock.cue pins providers that are installed. |
| Changes infrastructure | No. |
| Taught in | Quick start, step 3, and B1, Providers and resources. |
Flags
| Flag | Default | Meaning |
|---|---|---|
--upgrade |
off | Ignore pravo.lock.cue and select the newest version that matches each constraint. |
-t, --tag key=value |
none | Inject a value for an @tag() field while the specification loads. Repeatable. |
-T, --inject-vars |
off | Inject the system variables of CUE, such as os, arch and username, into tags written @tag(name,var=os). |
The global flags apply. A single dash works as well: -upgrade.
Exit codes
| Code | Meaning |
|---|---|
| 0 | The root is initialised. |
| 1 | A provider could not be resolved or installed, the registry could not be reached, or an argument was given: init takes none. |
| 6 | The specification does not load or evaluate. |
What init writes
| Path | Content |
|---|---|
cue.mod/module.cue |
The CUE module file. Written only when no module exists in the root or above it. The module name is pravo.local/ and the name of the directory. |
cue.mod/gen/pravo.dev/providers/<namespace>/<type>/ |
One package per provider. It holds every resource, data source, ephemeral resource and function of that provider. |
cue.mod/gen/pravo.dev/pravo/ |
The package pravo.dev/pravo: #Settings, #Output, #Env, #SecretEnv, #RemoteOutputs and #Movable. |
pravo.lock.cue |
The provider versions and archive hashes in use, and the hash of each dependency module. Commit it. It is written in the layout of pravo fmt. |
Where providers are kept
init keeps downloaded providers and the schemas it reads from them in one directory, $PRAVO_HOME. It defaults to ~/.pravo. Every root on the machine shares it.
Path below $PRAVO_HOME |
Holds |
|---|---|
providers/<host>/<namespace>/<type>/<version>/<os>_<arch>/ |
The provider binary. |
schemas/<host>/<namespace>/<type>/<version>.json |
The cached schema of that provider version. |
s3-probe/ |
One empty file for each S3 store that passed the check for conditional requests. |
Set PRAVO_HOME to move the directory, for example to a cache directory on a build machine. Later commands in that root need the same PRAVO_HOME.
If you run OpenTofu, its plugin cache can serve the providers. Before it downloads, init looks for the selected version in $TF_PLUGIN_CACHE_DIR and in ~/.terraform.d/plugin-cache. Both use the layout <host>/<namespace>/<type>/<version>/<os>_<arch>/. pravo uses such a binary where it lies and does not copy it. If you found it through TF_PLUGIN_CACHE_DIR, keep that variable set for every later pravo command.
init reads the import lines of the .cue files and the literal $version, $path, $protocol and $address fields of each provider struct. A $version that is computed instead of written as a string is not seen. The fields are covered in Provider declarations. It reads $settings before the generated packages exist, with the tags given by -t and -T.
Examples
Work in an empty directory named demo.
A directory without a specification
A directory with no .cue file has nothing to load. init creates the module and the package pravo.dev/pravo, says so and exits 0.
Created cue.mod/module.cue
There is no specification yet. Write a .cue file in this directory, then run "pravo eval" or "pravo validate".
Write a .cue file, then run pravo eval or pravo validate. A file that imports a provider needs pravo init again.
A state path that depends on a tag
init reads the $settings block before the packages it imports exist. A tag that has no default needs a value there too, and -t gives it:
package tagged
import (
"pravo.dev/pravo"
rnd "pravo.dev/providers/hashicorp/random"
)
env: string @tag(env)
$settings: pravo.#Settings & {state: path: "state/\(env)"}
$random: rnd.#Provider
Configuration does not load yet; generating schemas for the declared providers first.
Created cue.mod/module.cue
Providers:
random = hashicorp/random v3.9.1
Up to date cue.mod/gen/pravo.dev/providers/hashicorp/random (10 resource types, 0 data sources, 2 ephemeral, 0 functions)
pravo has been initialised. Run "pravo plan" to see what it would do.
A root without providers
A root that imports no generated package needs no provider. init creates the module file.
package demo
size: int
size: 3
Providers:
Created cue.mod/module.cue
pravo has been initialised. Run "pravo plan" to see what it would do.
pravo validate and pravo eval evaluate such a file with or without init. No provider is involved.
size: 3
module: "pravo.local/demo"
language: version: "v0.14.0"
A root with providers
Replace main.cue with a specification that imports two generated packages. The packages do not exist yet.
package demo
import (
rnd "pravo.dev/providers/hashicorp/random"
loc "pravo.dev/providers/hashicorp/local"
)
$random: rnd.#Provider
$local: loc.#Provider
Configuration does not load yet; generating schemas for the declared providers first.
Providers:
local = hashicorp/local v2.9.1
random = hashicorp/random v3.9.1
Up to date cue.mod/gen/pravo.dev/providers/hashicorp/local (2 resource types, 3 data sources, 1 ephemeral, 1 functions)
Up to date cue.mod/gen/pravo.dev/providers/hashicorp/random (10 resource types, 0 data sources, 2 ephemeral, 0 functions)
pravo has been initialised. Run "pravo plan" to see what it would do.
The first line appears when the imports cannot resolve yet. init generates the packages for the declared providers, then loads the specification again. On a first run each provider also prints a Downloading line.
cue.mod/gen/pravo.dev/pravo/settings_gen.cue
cue.mod/gen/pravo.dev/providers/hashicorp/local/local_gen.cue
cue.mod/gen/pravo.dev/providers/hashicorp/random/random_gen.cue
cue.mod/module.cue
pravo.lock.cue
The lock file
// This file is maintained by "pravo init". Commit it to version control.
providers: "registry.opentofu.org/hashicorp/local": {
version: "2.9.1"
hashes: {
"<os>_<arch>": "sha256:<hash>"
}
}
providers: "registry.opentofu.org/hashicorp/random": {
pravo fmt prints the name of each file it rewrites. Right after init it prints nothing for the lock file:
The hashes name the platforms for which an archive was installed. pravo providers lock adds the other platforms.
Run it again, offline
With the lock file present and the providers installed, init makes no network request. The proxy below refuses every connection, and the command succeeds.
pravo has been initialised. Run "pravo plan" to see what it would do.
Without the lock file, init asks the registry for versions and fails when it cannot reach it:
Error: registry discovery for registry.opentofu.org: Get "https://registry.opentofu.org/.well-known/terraform.json": proxyconnect tcp: dial tcp 127.0.0.1:9: connect: connection refused
Upgrade
--upgrade ignores the lock file, asks the registry for the newest version that matches each $version constraint, and rewrites the lock file.
pravo has been initialised. Run "pravo plan" to see what it would do.
Behaviour to know
- Every other command needs the generated packages. Run
pravo initafter a checkout, or commitcue.mod/gen. - A generated package holds every type of its provider, so one package serves every root and module that imports it. Regenerate the packages with
pravo gen. - In a repository with one CUE module at the top,
initfinds that module and does not create a nested one. See Modules in a repository. initaccepts a dependency module whose content differs frompravo.lock.cueand records the new hash. Every other command refuses it with exit code 5.
See also
pravo providerslists the resolved providers.- Generated packages describes the files under
cue.mod/gen. - Providers from a registry shows a provider from a registry other than the default.