ENV variables for motoko builds

Is there a way to include ENV variables in the motoko build process? I know passing variables to an actor class could be a workaround, but sometimes you might want to have a “hardcoded” value that’s not passed to an instantiation of an actor. If this is not available yet, are there plans to add support?

@diegop @claudio

4 Likes

I haven’t tried this, but I could imagine a “hacky” process where you have multiple dfx.json files (i.e. local.dfx.json, dev.dfx.json, prod.dfx.json), then feed the env you want to a Python/bash script where this script would then copy in the correct (local/dev/prod).dfx.json as the root dfx.json and then execute the dfx build.

Then, you can similarly have localEnvs.mo, devEnvs.mo, and prodEnvs.mo module files and then as part of the same Python/bash script above copy the appropriate environment file to a envs.mo that your application is expecting. Then have your other application actor/module files import from “./src/envs”, and be good to go.

For testing, you would need a Makefile with make test local, make test dev, make test prod, etc. that would copy in the relevant files and then run the tests.

If you’re talking about environment variable injection through the actor I have no clue how to do that, and would love to see this feature (through the dfx sdk?) as well, as the process I described sounds like a repeated and unnecessary pain for others to complete.

I am not sure, but I have pinged people internally. Good question

Do you have a specific example in mind? What kinds of values would need to be supported? Simple Text values, or something with a more involved Motoko/Candid type?

2 Likes

I think the short answer to the original question is no and we don’t have any current plans to support that. Is there any precedence from other languages?

As a workaround, your best bet might be to use a script to generate a .mo file containing the constants derived from environment variables or some other pre-processor.

2 Likes

Almost state of the art with any JavaScript framework - e.g. dotenv that allows such mechanism hits 25 millions download weekly on npm

1 Like

Not speaking for @cryptoschindler, but as a starter just a configurable -env command line parameter like -env “local”, or -env=local would be nice to start.

Sort of like what webpack has Environment Variables | webpack

Next, a Motoko equivalent of JSON config file that could be passed to moc/dfx would be great, and then these environment variable files could be accessible for use, without being publicly visible (i.e. secret environment variables don’t need to be stored publicly in in the same repo, but can be injected as part of the build/compile process and are now stable and persist through upgrades).

-envFile “envs/local.mo” or -envFile=“envs/local.mo”

1 Like

moc has a --package flag which could be used like this:

env/lib.mo

module {
  public let SECRET = "MY_SECRET";
};

main.mo

import Debug "mo:base/Debug";
import Env "mo:env";

Debug.print(Env.SECRET);

moc --package base $(dfx cache show)/base --package env env -r main.mo

Note that the -r flag runs the file. Depending on your use case you may want to use -c.

4 Likes

This is great! Would be nice if the —-package option could be added to dfx build. Then I can include this in CI.

You can probably do that by using the custom canister type in dfx.json. Something like;

{
  "canisters": {
    "foo": {
      "type": "custom",
      "build": "moc -c …",
      "candid": "src/foo/src/foo.did",
      "wasm": "foo.wasm"
    }
  }
}
2 Likes

Simple text would be enough for me needs.

An example would be an actor that I want to open source and already use myself which contains sensible information like addresses or principals I don’t want to leak.

That seems pretty cumbersome, and as @peterparker mentioned it seems to be pretty common at least on the frontend.

1 Like

Thanks, this should do the trick :tada:

hi, could you provide more information about the moc command ?

I think if you just want to normally deploy your canister you can ignore the moc stuff. Just create your module that contains your env variables and add it to your .gitignore. Whenever you need one of the env variables in an actor or another modules just import the module the “normal” way, e.g. import Env "Env"; if you have a Env.mo file in the same directory. If another user clones your repo this file will be missing and the actor won’t compile, so you can tell consumers to create it and add their own env variables to it.

moc is the Motoko compiler. dfx issues moc commands when building Motoko canisters. moc --help shows usage information.

1 Like

@paulyoung is this correct or is there a twist to it using --package?

@cryptoschindler what you said sounds right to me.

1 Like

The benefit of is that you can import from any file without dealing with relative file paths.

1 Like

After reconsidering it would still be nice to be able to distinct between production and development. For local development for example I need to specify the local canister ID, for production the mainnet ID. I don’t want to maintain two different files for that so currently I use actor class parameters and switch statements, which doesn’t feel very natural.