Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for compile-time vs runtime variables #1217

Closed
gwvt opened this issue May 3, 2018 · 6 comments
Closed

support for compile-time vs runtime variables #1217

gwvt opened this issue May 3, 2018 · 6 comments

Comments

@gwvt
Copy link

gwvt commented May 3, 2018

What problem does this feature solve?

When developing in a Heroku pipeline, environment variables are hardcoded in the build process for a staging app. When that app is promoted to production, the build is not recompiled, and there is no way to change the values passed to those variables. For example, the value of a variable like VUE_APP_API_URL will likely need to change from the staging API/database to the production API/database. Using vue-cli modes and setting different values in .env.staging and .env.production only works if the app is being recompiled for the production environment.

What does the proposed API look like?

See the solution implemented for create-react-app as outlined here: https://github.com/mars/create-react-app-buildpack#compile-time-vs-runtime.

@yyx990803
Copy link
Member

This is too-tightly coupled to the constraints of a specific platform. Also looks like this is possible to solve in userland, so it should be done in userland.

@essenmitsosse
Copy link

essenmitsosse commented Jun 12, 2018

I don't think its specific to Heroku.

Deploying an SPA to use a staging API and then later deploying it to production seems like a pretty common workflow. Right now, there is no elegant workflow to deploy to different environments, without having to rebuild, thus ending up with different builds for different environments, when the only difference is the API Url.

There is also a discussion surrounding the topic over here: facebook/create-react-app#578 (comment)

I feel like this is a topic a web app framework should address, but everybody tries to avoid. At least some best practice guidelines would be great.

maelvls added a commit to maelvls/touist-editor that referenced this issue Jun 17, 2018
@Spittal
Copy link

Spittal commented Jul 6, 2018

I have been plagued by this problem for many years now, especially since we now use ephemeral review environments in our Kubernetes cluster.

At first, I just dealt with it, "no problem" I said "we can just run webpack before serving up requests". This works fine, but there's a fundamental problem, running a static site requires significantly less RAM and CPU than running webpack on a large project. So, you are now provisioning environments that have huge CPU and RAM so you can compile webpack just in time. This is expensive, and honestly a terrible idea.

Then I thought to myself "Why don't I just create a simple node script that just finds and replaces all instances of process.env.* with real env variables. it can be super lightweight!". This also failed, not only because I gave up on the idea after about 10 minutes, but also because node's fs is pretty resource hungry. So, same problem.

I ended up solving this issue, as Evan suggested above, in userland. It's not an ideal setup by any means, but I can now compile my entire app in a continuous deployment pipeline, bake that code into a Docker container and run the app anywhere with no issues. The solution I came to is regex matching the hostname and changing the environments variables that way. here is the actual code I'm using.

export default function configureEnvironment () {
  window.sbvrenv = {};

  const hostname = window && window.location && window.location.hostname;

  if (/^.*.springboardvr.docker/.test(hostname) || /^.*localhost.*/.test(hostname)) {
    window.sbvrenv.NODE_ENV = 'development';
    window.sbvrenv.API_URL = 'api.springboardvr.docker';
    window.sbvrenv.GRAPHQL_URL = 'graphql.springboardvr.docker';
    window.sbvrenv.PANEL_URL = 'app.springboardvr.docker';
    window.sbvrenv.MONITOR_URL = 'monitor.springboardvr.docker';
    window.sbvrenv.SOCKET_URL = 'socket.springboardvr.docker';
    window.sbvrenv.OPERATOR_URL = 'operator.springboardvr.docker';
  } else if (/^staging-.*.springboardvr.com/.test(hostname) || /^review-.*.springboardvr.com/.test(hostname)) {
    window.sbvrenv.NODE_ENV = 'staging';
    window.sbvrenv.API_URL = 'staging-api.springboardvr.com';
    window.sbvrenv.GRAPHQL_URL = 'staging-graphql.springboardvr.com';
    window.sbvrenv.PANEL_URL = 'staging-app.springboardvr.com';
    window.sbvrenv.MONITOR_URL = 'staging-monitor.springboardvr.com';
    window.sbvrenv.SOCKET_URL = 'staging-socket.springboardvr.com';
    window.sbvrenv.OPERATOR_URL = 'staging-operator.springboardvr.com';
  } else {
    window.sbvrenv.NODE_ENV = 'production';
    window.sbvrenv.API_URL = 'api.springboardvr.com';
    window.sbvrenv.GRAPHQL_URL = 'graphql.springboardvr.com';
    window.sbvrenv.PANEL_URL = 'app.springboardvr.com';
    window.sbvrenv.MONITOR_URL = 'monitor.springboardvr.com';
    window.sbvrenv.SOCKET_URL = 'socket.springboardvr.com';
    window.sbvrenv.OPERATOR_URL = 'operator.springboardvr.com';
  }
}

I hope this helps some people. <3

@AlexandreBonaventure
Copy link
Contributor

Obviously this is a trade-off because you can't benefit the dead-code elimation at build-time with UglifyJS, however in most cases if you are looking into improve build size you can use async imports and code splitting.

@assembledadam
Copy link

Run-time variables are a must for me too.

I solved this by using JS config variables within the app (so that when the app is compiled, it refers to e.g. window.appConfig.API_ROOT).

Then within the CI pipeline, a JS file is created defining all the config values for that particular environment.

window.appConfig = {
  apiRoot: 'http://api.example.com',
  ...
}

In reality it's a little more complex as the JS file within the CI workflow grabs the config values from a centralised secrets store.

@dehypnosis
Copy link

dehypnosis commented Dec 6, 2018

For SSR and browser both side,

src/app/runtime-config.js

an isomorphic module to fetch a config json file in runtime (after build in production mode)

import 'isomorphic-fetch'
import serverConfig from '../../vue.config'

const IS_SSR = process.server
const WEB_SERVER_URL = IS_SSR ? `http://${serverConfig.pluginOptions.ssr.host}:${serverConfig.pluginOptions.ssr.port}` : location.origin
const RUNTIME_CONFIG_URL = `${WEB_SERVER_URL}${process.env.BASE_URL}config/runtime.json`
let cachedConfig = null

export default async function getRuntimeConfig () {
  if (cachedConfig) return cachedConfig

  return fetch(RUNTIME_CONFIG_URL)
    .then(res => res.json())
    .then(config => {
      cachedConfig = IS_SSR ? config.ssr : config.browser
      console.log(cachedConfig)
      return cachedConfig
    })
}

public/config/runtime.json

a config json example

{
  "browser": {
    "GRAPHQL_SERVER_URL": "https://api.dev.redacted--.kr/graphql"
  },
  "ssr": {
    "GRAPHQL_SERVER_URL": "http://api-gateway.dev.svc.cluster.local/graphql"
  }
}

src/app/any-module.js

usage example

import getRuntimeConfig from './runtime-config'

...
const config = await getRuntimeConfig()
const HTTP_ENDPOINT = config.GRAPHQL_SERVER_URL

It works!
To change configuration dynamically, can override dist/config/runtime.json after production build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants