envless-env types reads the names, types and visibility of your variables, never their values, and writes sig/envless-env.rbs: signatures that type Envless.env by name. A misspelt name, a server variable read through the client view, or an Integer used as a String then fails steep check instead of your deploy.
bundle exec envless-env typesrequire "envless/env/load"
database_url = Envless.env.DATABASE_URL
port = Envless.env[:PORT]
public_url = Envless.env.client.PUBLIC_API_URL
sentry_dsn = Envless.env.optional.SENTRY_DSN
puts database_url.length, port + 1, public_url.length, sentry_dsn.to_sIt is the Ruby counterpart of envless types, which writes envless-env.d.ts for TypeScript. The file holds signatures only, so it changes nothing at run time. Commit it, it holds no secrets. Steep reads it next to the gem's own signatures:
target :app do
signature "sig"
check "app", "lib"
library "envless-sdk", "envless-env"
endWhat it writes
| Read | Typed |
|---|---|
Envless.env.NAME, Envless.env[:NAME] | Every variable, by its type |
Envless.env.optional.NAME | Every variable, each with nil |
Envless.env.client.NAME | Client variables only |
Envless.env.client.optional.NAME | Client variables only, each with nil |
A string variable is typed String, a boolean is bool, and a number is Integer | Float, because a number reads back as an Integer when it is whole and a Float when it is not. Narrow it with is_a?(Integer) where an API wants a whole number.
Method reads such as Envless.env.DATABASE_URL exist for names that start with a capital letter, the same rule the runtime follows, so a misspelt one is a missing method in Steep. Envless.env[:NAME] is typed for every name that is a valid Symbol, and still accepts any other String or Symbol as String | Integer | Float | bool | nil, so a name you build at run time keeps working. Names that are not valid Symbols, my-var for instance, are listed in a comment at the top of the file and stay readable as Envless.env["my-var"].
The views are typed Envless::Env::Typed::Server, Optional, Client and ClientOptional. Those classes exist only in the signatures: at run time every view is an Envless::Env::Values.
Where the names come from
Under envless run, the command reads the manifest the CLI injected and makes no request, so your envless login session is all it needs:
envless run -- bundle exec envless-env typesOutside it, the command fetches the metadata with the same machine credentials the loader uses: ENVLESS_TOKEN, with the project and environments from the flags, from ENVLESS_PROJECT and ENVLESS_ENV, or from .envless. No decryption key is needed. Every environment in .envless is read and merged in order, so a name that only exists in staging is still typed, with the type of the first environment that declares it.
| Flag | Purpose |
|---|---|
--output PATH | The file to write, sig/envless-env.rbs by default |
--project SLUG | The project, instead of ENVLESS_PROJECT or .envless |
--product SLUG | The product, when the project slug exists in more than one |
--env SLUGS | Comma separated environments, merged in order |
--check | Exit 1 when the file is missing or stale, and write nothing |
A failed fetch exits 1 with the loader's own message, Missing ENVLESS_TOKEN for instance, and an unknown flag exits 2.
Keeping it current
Run it again whenever you add, rename or retype a variable. In CI, --check fails the job when the committed file no longer matches Envless:
envless run --env production -- bundle exec envless-env types --checkThe file is sorted by name, so a variable added in Envless shows up as one small hunk in review. Leave it out of hand edits: the next run replaces it.