Managing Environments

There doesn’t seem to be a good way to manage different packs in the same pack directory.

My usecase is that I need to be able to develop my pack and test changes in coda, while also supporting a “stable” release.

I’ve worked around this by hardcoding a config object and then indexing its values depending on another hardcoded env const:

// index.ts
import { codaPack, buildPack } from "./pack";

const configs = {
  dev: {
    baseAuthUrl: "https://dev.auth.us-east-1.amazoncognito.com",
    baseApiUrl: `https://dev-api.execute-api.us-east-1.amazonaws.com/DEV/`,
  },
  test: {
    baseAuthUrl: "https://test.auth.us-east-1.amazoncognito.com",
    baseApiUrl: `https://test.execute-api.us-east-1.amazonaws.com/TEST/`,
  },
  prod: {
    baseAuthUrl: "https://prod.auth.com",
    baseApiUrl: `https://prod.execute-api.us-east-1.amazonaws.com/PROD/`,
  },
};

const env = "dev";
// const env = "test";
// const env = "prod";

const config = configs[env];

export const pack = codaPack;
buildPack(pack, config.baseApiUrl, config.baseAuthUrl);

This obviously isn’t ideal, but it helps me get around the issue.

I also had to create two packs, one for my test/dev work, and one for prod.
I did this by running npx coda create twice, and then taking the generated .coda-pack.json file and naming it .coda-pack.dev.json and .coda-pack.prod.json. Then in my build script, I cp the desired working config to the .coda-pack.json:

// package.json
{
  ...
  "scripts": {
    "deploy": "cp config/.coda-pack.dev.json .coda-pack.json && npx coda upload index.js",
    "deploy:prod": "cp config/.coda-pack.prod.json .coda-pack.json && npx coda upload index.js",
    ...
  },
  ...
}

This is a very clunky workaround and could be easily fixed by allowing developers to pass an --env {my_env} into the npx coda upload command that could then be queried from the process node variable.
The command should also be updated to allow passing in the .coda-pack.json file path so that I can configure the deployment to use the proper dev or prod pack id:

// package.json
{
  ...
  "scripts": {
    "deploy": "npx coda upload index.js --env dev --pack config/.coda-pack.dev.json",
    "deploy:prod": "npx coda upload index.js --env prod --pack config/.coda-pack.prod.json",
    ...
  },
  ...
}

Or, another option would be to allow an ambiguous amount of variables to reside in the .coda-pack.json file where I can define my URLs for each environment and associated pack id.

// .coda-pack.dev.json
{
  "packId": 1,
  "env": {
    "authUrl": "https://auth.url",
    "apiUrl": "https://api.url"
  }
}

These values would then be exposed in the process.env object.

Cheers.
~ CG

1 Like

Hi @Chris_Gregory - Thanks for raising this concern, it’s one I’ve heard from a few other people as well. I think the team primarily envisioned that Pack makers would use the release system built into the platform to allow for the development of new features without affecting existing users.

It works well for simple workflows, but doesn’t scale to some more complex scenarios:

  • Test a large refactor in a development branch while releasing bug fixes in the main branch.
  • Test the Pack against an API’s sandbox environment while having users operate it in the API’s production environment.

I’d agree that the tooling today doesn’t provide a lot of support deploying to different environments, and I’ll pass that feedback on to the team. The workaround you are using is creative, but I can see how it would feel clunky. I played around with a slightly different model that looks like this:

pack.ts
env/
  prod/
    .coda-pack.json
    pack.ts
  test/
    .coda-pack.json
    pack.ts    
  dev/
    .coda-pack.json
    pack.ts    

The root pack.ts is your normal Pack code, but which also exports a config object.

import * as coda from "@codahq/packs-sdk";
export const pack = coda.newPack();

interface Config {
  greeting: string;
}
export let config: Config = {
  greeting: "Hello",
};

pack.addFormula({
  name: "Greet",
  description: "Greets someone.",
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: "name",
      description: "Their name.",
    }),
  ],
  resultType: coda.ValueType.String,
  execute: async function ([name], context) {
    return `${config.greeting} ${name}`;
  },
});

The environment-specific pack.ts files then just re-export the root file’s Pack declarations, and optionally change some of the config values.

import * as main from "../../pack";
export const pack = main.pack;

main.config.greeting = "Howdy";

Each environment folder has it’s own .coda-pack.json file with the unique Pack ID to deploy to, so uploading doesn’t require any scripts:

npx coda upload env/prod/pack.ts

What do you think about that pattern?

1 Like

@Eric_Koleda That does seem like a cleaner workflow than what I was doing. I will take a loot at potentially implementing that.

To get a new packId, is it best practice to run npx coda create for each env (then clean up the generated node_modules, and other files to just be left with the new id)?

Thanks for your detailed response, I appreciate it.

npx coda create just creates the Pack and generated the .coda-pack.json file. It doesn’t install node modules or do other setup like coda init does. The option is to create the Pack in the Pack Studio and then run npx coda link to just generate the .coda-pack.json file that points to it.

2 Likes

Worked beautifully. Thank you for the suggestion :slight_smile:

1 Like

Hey @Eric_Koleda,
Do you know if I can “copy” one sync tables data to another?
In my pack we have a sync table with a lot of rows with a few different formula in each row, and now with multiple environments, it’s a very manual process to set up each table. Do you know of any way to make it easier to copy data from a identical table schema to another?

Hi @Chris_Gregory - No, there isn’t any way that I know of to do that. I imagine it’s just a one-time setup though?