How to Manage Staging and Production Environments in a React Native App - react-native

I got a react-native app already ready and works but the app points on a prod api so to avoid problems I wanted that she points on a rest api, so how can I reconfigure it, I have only an environment file.js that contains the URL api, and this file is called in the API service
I tried react-native config but didn't know how to make it work,
All I want is to duplicate that env file because he contains all necessary URLs, so I want to have file named envProd and another named envStaging they should be similar the only different part is the URL of my api , so when I want to run the app i precise which file I want to choose to run with

If the API URL's are the only difference, you don't need to create two separate config files. Instead, you can make use of the inbuild __DEV__ global variable in JavaScript to determine if you're using React Native packager (dev mode) or not (production).
Ref: https://facebook.github.io/react-native/docs/javascript-environment.html
So, something like below.
var apiURL = __DEV__ == true ? 'development.api.com' : 'prod.api.com';

I am finding switching environments quite hard to manage.
__DEV__ is awesome for local disabling stuff when in development. Won't work for any of the builds though.
For staging vs production seems that configuring react-native-config is the best. Unfortunately, next to being somewhat hard to configure binaries need to get recompiled separately for each environment. This means - one update - two build with uploads to whatever provider you use. It can get more complex if you use something like codepush too.
possible solution
Create a hidden feature in your app that allows to toggle the environment from the within the app. This way you can have one build, give it to testers. They switch env to staging and later you only need to promote it as a production build.
This could be something like tap 10 times on an empty space somewhere - the more creative you get the better :)
Good luck

const _Environments = {
production: {
API_BASE: 'https://api.test.com/',
USERNAME: 'admin',
PASSWORD: '1234',
},
development: {
API_BASE: 'https://dev.api.test.com/',
USERNAME: 'admin',
PASSWORD: '1234',
},
};
function getEnvironment() {
// Insert logic here to get the current platform (e.g. staging, production, etc)
var platform = __DEV__ ? 'development' : 'production';
// ...now return the correct environment
return _Environments[platform];
}
export const Environment = getEnvironment();

Try this. using yargs you can replace static import content with dynamic scripts.

Related

Expo application doesn't get changes in .env file

I have an Expo managed react native application. I created my .env file in the root of my project, installed react-native-dotenv and set up babel to use it. After a while I managed to get it to work.
I have my environment variable
ENDPOINT=http://127.0.0.1:8000/api
and i use it with
process.env.ENDPOINT
After a while I decided to test the android version of the app, so i changed the endpoint url to my LAN ip and restarted the server. The problem is that even after restarting the server, the cache and the computer, when I call process.env.ENDPOINT it keeps the first url I set.
Here's a list of the things i tried:
restarting the server
restarting the server and the cache
restarting the whole computer
change the variable name to REACT_APP_ENDPOINT as many suggested (I get undefined, it's still stuck to ENDPOINT)
empty expo cache
The strange thing is that I already changed that same variable twice (from 127.0.0.1:8000 to 127.0.0.1:8080 and back for a problem with backend) and had the same problem, but it went away by itself after a couple of minutes (and server restarts).
This time I've been trying to get it to work for 7 hours and nothing has changed.
Any idea?
I had the same issue and managed to run the app with .env changes after using the following command.
expo r -c
reference: https://github.com/goatandsheep/react-native-dotenv/issues/75#issuecomment-728055969
After a couple hundred more tests I gave up and implemented a "custom" solution, without any external library:
Switched .env files to TypeScript files (E.g. .env.development -> env.development.ts)
Set up an object named env that has all environmental variables as properties
export const env = {
VAR1: 'foo',
...
}
Imported this constant inside the application entry point (in my case App.tsx)
Inside the main constructor assign env to global.env
Use global.env instead of process.env
Not sure if this is the best practice, but solved my problem for now, works like a charm and doesn't require me to reload my application at every change. I'm a bit concerned by the security aspect of having the environment in a global variable inside a js project, so any suggestion is still welcome

Is there a way to change the browser locale in Cypress?

I have looked around online and quite a bit on stackoverflow and cant seem to find an up-to-date answer for this question. I have found other questions here, here, and here - and none of those solution work anymore. Does anyone know if this functionality is available in Cypress or if there's a way to force a locale change? Thanks
That's not too easy - as this differs from the browser you will be running your tests in.
Also, I believe that only are testing that your application looks and behaves right when the USER selected a specific language, you should consider adding a parameter when initiating your test session, e.g.
http://myapp.com/start?lang=en
then storing the selected language in LocaleStorage or a Cookie and selecting the language from there, as it is a lot more portable - and you will have users wanting to use your application in a different language than the browser default.
Still, There might be a few scenarios (e.g. if you want to tests, that your application detects the browser language correctly) where setting the browser language can be required, but I would suggest keeping tests that rely on a browser configuration to a minimum.
Is is possible tho, at least for Electron (verified with Electron 100) and Firefox (verified with Firefox 102). It used to work in Chrome too, but doesn't work for me anymore.
For Electron:
As Electron is being started along with the cypress GUI, changing the language afterwards is not possible. But you can expose an environment variable to the process, e.g.
ELECTRON_EXTRA_LAUNCH_ARGS=--lang=en npx cypress open
to start electron with the wanted locale.
For Firefox:
In your cypress.config.ts expand the defineConfig to contain such a block:
export default defineConfig({
// ...
e2e: {
// ....
setupNodeEvents(on, config) {
on('before:browser:launch', (browser, launchOptions) => {
if (browser.family === 'firefox') {
launchOptions.preferences['intl.locale.requested'] = "en_US"
return launchOptions
}
}
}
}
The Firefox Setup will only work, if you have no other Firefox instance running in another locale.

How can I test electron-builder auto-update flow?

I built an Electron app and I am now looking at how to distribute it.
I went with electron-builder to handle packaging etc.
For a bit of context, as a web developer, I am used to continuously deploy web apps on a web server but I have a hard time figuring out how to distribute a packaged one in Electron.
In electron-builder docs there is a brief mention about testing auto-update:
"Note that in order to develop/test UI/UX of updating without packaging the application you need to have a file named dev-app-update.yml in the root of your project, which matches your publish setting from electron-builder config (but in YAML format)"
But, it's rather vague...
So I actually have two questions:
1. How do I actually test the auto-update flow?
Do I need to actually publish a new version to trigger an update locally? Seems pretty unclear, it would be like developing against the production server.
2. Is it possible to have a fallback for unsigned code?
I don't have yet any certificate for code signing. So the OS/app will block the auto-update. But, I'd still want to tell the user that an update is available so they can go and download the app manually. Can I do that? (going back to point 1, I'd like to be able to test this flow)
I've just finished dealing with this. I also wanted to test against a non-production server and avoid having to package my app each time I iterated. To test downloads I had to sign my app, which slowed things down. But it sounds like you just need to check for updates. Which I think you can do as follows...
I created a dummy github repo, then created a a file dev-app-update.yml containing:
owner: <user or organization name>
repo: dev-auto-update-testing
provider: github
The path where this file is expected to be defaults to a place you can't access. Thankfully, you can override it like so:
if (isDev) {
// Useful for some dev/debugging tasks, but download can
// not be validated becuase dev app is not signed
autoUpdater.updateConfigPath = path.join(__dirname, 'dev-app-update.yml');
}
...that should be enough for your case -- since you don't need downloads.
If not, here are some other tips:
you can change the repo setting in your electron-builder config to point at your dummy repo then package your app. This will give you a packed, production build that points at your dummy repo -- this is how I did my download testing (though I have a cert, and signed my app)
you should be calling autoUpdate's checkForUpdates(), but if checkForUpdatesAndNotify() gives you a useful OS Notification then you should be able to set autoUpdater.autoDownload to false and end up with what you need.
Lastly, it sounds you could skip autoUpdater, since you won't be using the download feature anyway. Instead you could use github's releases api, assuming you use github to host your release. If not then your host should have something similar. Use that to check for updates then tell the user from within your App (could present them with a clickable URL too). If you want OS Notifications electron has a module for that.
We're using electron-updater with GitHub as a provider for auto-updates. Unfortunately, it breaks a lot and the electron-builder team doesn't support these issues well (1, 2, 3) (from my own experience, but you can find more examples on GitHub).
One way to test updates in dev mode:
Create a build of your app with an arbitrarily high version number
Create a public repo and publish the above build
Create a dev-app-update.yml next to your main entry point and configure it for the repo above (see)
In your main entry point:
import { autoUpdater } from "electron-updater";
...
if (process.env.NODE_ENV === "development") {
// Customize the test by toggling these lines
// autoUpdater.autoDownload = false
// autoUpdater.autoInstallOnAppQuit = false;
autoUpdater.checkForUpdates();
}
Then when running yarn dev you should see something like:
Checking for update
...
Found version 100.0.0 (url: <>.exe)
Downloading update from <>.exe
updaterCacheDirName is not specified in app-update.yml Was app build using at least electron-builder 20.34.0?
updater cache dir: C:\Users\<>\AppData\Local\Electron
New version 100.0.0 has been downloaded to C:\Users\<>\AppData\Local\Electron\pending\<>.exe
And it should install when you close the dev app.
This should give you some certainty but we still ran into issues in production. If you want to be sure, play through the full update flow with a test repo but packaged production apps just as you would do with the live one.

Can I change the process.env.NODE_ENV in dev?

I'm using a node package API client in my react-native project that uses the process.env.NODE_ENV to determine whether to return development or production data. I'd like to test it in my dev environment (simulator) against the production database (it's read-only and the data greatly differs between dev and prod).
From what I can tell from this:
https://github.com/reactjs/react-redux/issues/39#issuecomment-131415769
process.env.NODE_ENV is set to development by default when the dev flag is true.
I've tried setting the NODE_ENV to production in my environment but it isn't picked up in the actual simulator (it still says development).
Is there some way to specify this when starting up or using run-*?
In your AppDelegate.m startup (before calling jsBundleForBundleRoot), add this:
[RCTBundleURLProvider sharedSettings].enableDev = false;
Or for Android, it should be something like this (untested):
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("js_dev_mode_debug", false);
Note that these change the defaults, which I believe exist beyond the execution of your app. So you'll probably need to run them again with true to reset them back to the default values.

what is the most reasonable way apply webpack to a full-stack node app?

i've been studying up on webpack for a couple of weeks now, and i've seen many examples of front end setups, and probably just this one setup for a backend.
i'm trying to setup a react app with a node back-end (e.g. express, koa, hapi, etc) where i would need at least one transpilation step for the back-end (e.g. babel, coffeescript, etc), and i think it would be nice to use webpack there for consistency vs adding another build mechanism to the mix (e.g. gulp, grunt, etc).
it would also be great if i could make changes to the backend and have the server restart automatically (watch style).
i'm wondering if the best way to do that is to basically have two distinct project setups with their own package.json and webpack.config files. maybe nest the back-end one under a server folder in the top level project folder, and use one or more script directives in the top level package.json file to control the two.
i guess i might have to proxy one server to the other to avoid CORS issues as well.
looking for any guidance from those more webpack battle tested than i for a decent setup.
regards,
tony.
Easiest way is to split this into two tasks: a build step that outputs to a folder (e.g. 'server'), then watch the output folder for changes and restart server task.
1. Build task
This can be in the same webpack.config as client building code - you can export an array and webpack will watch all of them.
Example webpack.config.js (top half is for server)
module.exports = [
{
name: 'server code, output to ./server',
entry: './index.js',
output: {
filename: './server/index.js'
},
target: 'node'
},
{
name: 'client side, output to ./public',
entry: './app.js',
output: {
filename: './public/app.js'
}
}
];
2.Watch Step
For the watch step, nodemon monitor changes and restart. Otherwise you could add a watch task to your server.js manually using something like fs.watch or node-watch.