I need to use different accounts provider's configurations when the meteor application runs as Development, Test or Production environment.
Since Meteor 1.3 these flags work out of the box:
Meteor.isDevelopment
Meteor.isProduction
Meteor.isTest
Meteor.isAppTest
On the server:
var inDevelopment = function () {
return process.env.NODE_ENV === "development";
};
var inProduction = function () {
return process.env.NODE_ENV === "production";
};
Meteor sets the environment variable NODE_ENV to "development" when you run meteor. In production, you can set the variable to whatever you want, otherwise it will default to "production".
Update: I created a smart package to allow this to work on the client and server.
mrt add allow-env
Just set permission rules in a server file.
allowEnv({
NODE_ENV: 1
});
You can use Meteor.settings coupled with the --settings option used when running meteor run or meteor deploy.
For example, to run in dev mode, create a JSON file, call it meteorConfigDev.json, and put the following in it:
{
"public" : {
"mode" : "dev"
},
"anotherProperty" : "anotherValue"
}
Run your app using
meteor --settings meteorConfigDev.json
On the server and on the client you can access the "mode" using:
Meteor.settings.public.mode //in this case it will be "dev"
Note that settings in "public" are available on both the server and the client whereas everything else (in this case "anotherProperty") is only available on the server.
You can then have different configuration files for your different environments.
Very easy. I am running my app on five (yes, five!) different environments. I simply use a switch statement on the ROOT_URL as shown below for four different environments. Of course, you can use an if-else if you only have two environments. Works on the server. Just make a new file called startup.js and use the code example below. Cheers!
switch (process.env.ROOT_URL) {
case "http://www.production.com/":
BLOCK OF CODE HERE
break;
case "http://www.staging.com/":
BLOCK OF CODE HERE
break;
case "http://www.development.com/":
BLOCK OF CODE HERE
break;
case "http://localhost:3000/":
BLOCK OF CODE HERE
break;
}
In general, the format for a switch statement in javascript is
switch(expression) {
case n:
code block
break;
case n:
code block
break;
default:
default code block
}
UPDATE: Note that Meteor now provides Meteor.absoluteUrl(), which is similar to process.env.ROOT_URL with the addition of extra functionality. See docs.
There is an open pull request at github which would allow for that. Comment/Vote for it, so it is more likely to get included!
A really messy way to accomplish this
https://github.com/possibilities/meteor-environment-hooks
note: the interface is OK IMHO, the implementation is messy
Related
We have been building our automation suite using our staging environment, but are going live soon and want to be ready to tell the project where to run (staging, production).
The only difference between the sites in the environments is the URL. My question is, from start to finish, how can I set the .page URL via a CLI option?
Right now, I have created an environment config file that holds our staging and production URLS and then I call the data into my test files. This is fine for now, but I will need to create a script with an option to set the environment at runtime without having to do a manual find and replace before kicking it off.
I've looked around online and find, what I believe, to be code snippets and general instructions, but I'm not a dev at heart and go crossed eyed. If I could get an ELI5 for this, that would be awesome.
Example of what I'm doing now:
const env = require('../environment_variables.json')
fixture `blog`
.page `${env.production}`
And then I change production to staging or vice versa manually before kicking off the suite.
Since the project will run from CICD, I would like to be able to do something like this in my CLI and script:
testcafe env=production
The env value will then be set where the .page call is for every test file.
Thanks!
There are different ways of doing this. I've used environment variables successfully in this situation, so I'll share this solution since it will solve your problem.
I create config.json in the root of the project:
{
"baseUrl": {
"dev": "https://dev.com/",
"staging": "https://staging.com/",
"prod": "https://prod.com/"
}
}
Then I create two helper functions somewhere like Helpers/env.js:
import config from '../config';
function getEnv () {
return process.env.TESTCAFE_ENV;
}
function getBaseUrl () {
return config.baseUrl[getEnv()];
}
export { getEnv, getBaseUrl };
Then in my test files in Tests/:
import { getBaseUrl } from '../Helpers/env';
const baseUrl = getBaseUrl();
fixture `Test Suite`
.page(baseUrl);
And that's it. Then when I need to run tests on the dev, I execute:
$ TESTCAFE_ENV=dev testcafe
for staging:
$ TESTCAFE_ENV=staging testcafe
and for production:
$ TESTCAFE_ENV=prod testcafe
In v1.20.0 and later, TestCafe offers a way to specify the baseUrl in the test run configuration. You can use this approach along with environment variables, see the following example:
.testcaferc.js
const BASE_URL_MAP = {
dev: 'https://dev.com/',
staging: 'https://staging.com/',
prod: 'https://prod.com/'
};
module.exports = {
baseUrl: BASE_URL_MAP[process.env.TESTCAFE_ENV]
};
Alternatively, you can use different configuration files for each of the required setups using the --config-file option.
I am coding a website with Next.js and I tried to add google Tag Manager.
I followed the tutorial on the Next.js Github example but for some reasons I can't access to my environment variables.
It says my variable is undefined.
I created a file .env.local on my project folder (at the same level as components, node_modules, pages, etc)
In this file I created a variable like this (test purpose) :
NEXT_PUBLIC_DB_HOST=localhost
And on my index page I tried this code :
console.log("test ", process.env.NEXT_PUBLIC_DB_HOST);
But in my console I get a "test undefined".
I tried to put my variable into an .env file instead, without success.
What I am doing wrong ?
This envs just works in Server Side. To access this envs in Client Side, you need declare in the next.config.js
This way:
module.exports = {
reactStrictMode: true,
env: {
BASE_URL: process.env.BASE_URL,
}
}
Create .env (all environments), .env.development (development environment), and .env.production (production environment).
Add the prefix NEXT_PUBLIC to all of your environment variables.
NEXT_PUBLIC_API_URL=http://localhost:3000/
Use with prefix process.env
process.env.NEXT_PUBLIC_API_URL
Stop the server and restart it:
npm run dev
I hope it works.
This solution for latest version of nextJs (above 9)
Restarting the server worked for me.
Edit & save .env.local
Stop the server and restart it, npm run dev
You should get an output on the next line like this:
> klout#0.1.0 dev
> next dev
Loaded env from [path]/.env.local
For those using NextJS +9 and looking for environment variables in the browser, you should use the NEXT_PUBLIC_ prefix. Example:
NEXT_PUBLIC_ANALYTICS_ID=123456789
See documentation for reference.
After spending countless hours on this, I found that there is a tiny little paragraph in both the pre and post nextjs 9.4 documentation:
(Pre-9.4) https://nextjs.org/docs/api-reference/next.config.js/environment-variables (same as this answer)
Next.js will replace process.env.customKey with 'my-value' at build time.
(^9.4) https://nextjs.org/docs/basic-features/environment-variables
In order to keep server-only secrets safe, Next.js replaces process.env.* with the correct values at build time.
Key words being BUILD TIME. This means you must have set these variables when running next build and not (just) at next start to be available for the client side to access these variables.
This is my next.config.js file.
/** #type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
env: {
BASE_URL: process.env.NEXT_PUBLIC_SITE_URL,
},
};
module.exports = nextConfig;
Restart the server and it worked fine. using Nextjs 12.1.0 with typescript
In my case, Im pasting REACT_APP_API_URL instead of NEXT_PUBLIC_API_URL.
Adding with the most recent version of the documentation on this, v12+.
Using the next.config.js file you can specify server and client variables:
module.exports = {
serverRuntimeConfig: {
// Will only be available on the server side
mySecret: 'secret',
secondSecret: process.env.SECOND_SECRET, // Pass through env variables
},
publicRuntimeConfig: {
// Will be available on both server and client
staticFolder: '/static',
},
}
You can still use an env.local file, and pass the variable in to the next.config.js file. For example:
publicRuntimeConfig: {
DB_URL: process.env.DB_URL
}
And then you can access the variable like this:
import getConfig from 'next/config';
const { publicRuntimeConfig } = getConfig();
publicRuntimeConfig.DB_URL;
I'm building and trying do deploying a packaged electron app. FOr the packaging i used
electron-packager
electron-installer-debian
electron-installer-dmg
electron-winstaller
and I'm facing a little issue where I have to store tha appa datas somewhere in my user computer.
I saw that the good practice is to use the the folder in the path that is returned by the electron method app.getPath('userData').
from the docs
It is The directory for storing the app's configuration files, which by default it is the appData directory appended with the app name.
%APPDATA% on Windows
$XDG_CONFIG_HOME or ~/.config on Linux
~/Library/Application Support on macOS
By my tests sometimes this folder is not created automatically when the app is installed and other times yes and I'm wondering if i should create it or not.
Right now i'm quitting the app if this folder isn't present in the pc with the following code
var DatasPath = app.getPath('userData')
if (!fs.existsSync(DatasPath)){
process.exit()
}
So the question is
should i create the DatasPath folder with fs.mkdirSync(DatasPath); when it is not present or it is 'bad practice to do so', and if I can create the folder i have to warning the user the i have just added that folder?
(Expanding my reply from a "comment" to an "answer")
i don't know if i'm supposed to create it or not so i automatically
make the app quit if there is not that folder
It seems you are taking "userData" too literally? It is not an actual "folder" named "userData – it is a path to where the operating system stores data for that application. Electron currently runs on 3 operating systems and each one does things differently. For our convenience, Electron hides those differences by creating the wrapper method app.getPath(name) so the same code will work on each OS.
Try this: put the line below in your main.js script:
console.log(app.getPath('userData'));
/Users/*********/Library/Application Support/MyCoolApp
(the "*********" will be your user account name.)
UPDATED:
Run the code below in main.js and then look in the folder specified by the "userData" path
const fs = require("fs");
const path = require('path');
var datasPath = app.getPath('userData')
var data = "I am the cheese"
var filePath = path.join(datasPath, "savedData.txt")
fs.writeFileSync(filePath, data)
At pathConfig.js
function getAppDataPath() {
switch (process.platform) {
case "darwin": {
return path.join(process.env.HOME, "Library", "Application Support", "myApp");
}
case "win32": {
return path.join(process.env.APPDATA, "myApp");
}
case "linux": {
return path.join(process.env.HOME, ".myApp");
}
default: {
console.log("Unsupported platform!");
process.exit(1);
}
}
}
const appPath = __dirname;
const appDataPath =
!process.env.NODE_ENV || process.env.NODE_ENV === "production"
? getAppDataPath() // Live Mode
: path.join(appPath, "AppData"); // Dev Mode
if (!fs.existsSync(appDataPath)) {
// If the AppData dir doesn't exist at expected Path. Then Create
// Maybe the case when the user runs the app first.
fs.mkdirSync(appDataPath);
}
In each operating system the appData folder has a different path and the perfect way of getting this path is by calling app.getPath('userData') in the main process.
But there is a package that can handle this for you, it stores data in a JSON file and update it in every change.
In my opinion this package is much better than handling everything by your self.
Read more :
https://www.npmjs.com/package/electron-data-holder
TinyTest seems to be concerned only with unit testing; however, may Meteor packages have UI elements, and it would be helpful to pull in a pre-crafted HTML file that exercises a widget. For instance, we might want to transform a <table> into a grid with DataTables.net, then test if the instantiation was correct.
How can external HTML files be used in a TinyTest?
package.js:
Package.onTest(function (api) {
api.use(packageName, where);
api.use(['tinytest', 'http'], where);
// TODO we should just bring in src/test.html - but how to do that with TinyTest?
api.addFiles('src/test.html', where); // this won't magically display the HTML anywhere
api.addFiles('meteor/test.js', where);
});
test.js:
Tinytest.addAsync('Visual check', function (test, done) {
var iconsDropZone = document.createElement('div');
document.body.appendChild(iconsDropZone);
// TODO ideally we'd get src/test.html straight from this repo, but no idea how to do this from TinyTest
HTTP.get('https://rawgit.com/FortAwesome/Font-Awesome/master/src/test.html', function callback(error, result) {
if (error) {
test.fail('Error getting the icons. Do we have an Internet connection to rawgit.com?');
} else {
iconsDropZone.innerHTML = result.content;
test.ok({message: 'Test passed if the icons look OK.'});
}
done();
});
});
I personally think TinyTest is not the right tool for the job! You may get away with finding out how to include the Asset package or writing your own file loader, but you'll soon face the problem of needing to query the DOM in your tests.
Here are some options I can think of:
Option 1:
You can get access to a fully rendered page by using xolvio:webdriver. If you include this package in your onTest block, then you should have access to wdio in your TinyTest tests. I say should as I don't use TinyTest at all but I designed the webdriver package to be usable by any framework. Follow the instructions on the package readme and then do something like this:
browser.
init().
url('https://rawgit.com/FortAwesome/Font-Awesome/master/src/test.html').
getSource(function(err, source) {
// you have a fully rendered source here and can compare to what you like
}).
end();
It's a heavyweight option but might be suitable for you.
Option 2:
If you're willing to move away from TinyTest, another option is to use Jasmine. It supports client unit testing so you can load up the unit that does the visuals and isolate it with a unit test.
Option 3:
You can create a test app around your package. So you would have:
/package
/package/src
/package/example
/package/example/main.html
/package/example/tests
/package/example/tests/driver.js
And now the example directory is a Meteor app. In main.html you would use your package and under tests directory you can use the framework of your choice (jasmine/mocha/cucumber) in combination with webdriver. I like this pattern for package development as you can test the package as it is intended to be used by apps.
Any ideas on how to implement Mixpanel analytics through segment.io, that can track for all production and non-production environments.
Right now I have created 3 different projects (dev, staging and production) on both mixpanel & segment.io. And traking them. But when I'm changing dev code and pushing to staging and prouction, it overwrites analytics main code.
I am not using ruby....I'm using javascript. Any suggestions? Will a config file that substitutes token work?
Thanks. I did some research. I do have a simpler way of implementing this if someone is not familiar with config files or not having access to those files.
I can have a if condition that I can use when initializing segment i/o.
var apikey;
if (window.location.host === "dev.xyz.com") {
apikey = <api_key>;
} else if (window.location.host === "staging.xyz.com") {
apikey = <api_key>;
} else if (window.location.host === "prod.com") {
apikey = <api_key>;
}
analytics.load(apikey);
Replace with respective api_keys from segment I/o . This works well.
A config file that substitutes tokens is the perfect solution.
You'll want to do something like this in the javascript snippet:
analytics.load("<%= config.segmentio.apiKey %>");
Where config is your dev settings on your dev machine, and staging/prod settings on staging and prod.
I would suggest that as part of your build step, you bake in a configuration variable that identifies the environment which your code is running inside - e.g. ['Dev', 'Staging', 'Production'].
You would then do something similar to what #monical has suggested except without using URL's in the mix:
var token;
switch(environment) {
case 'Staging':
token = 'TOKEN_STAGE';
break;
case 'Production':
token = 'TOKEN_PROD';
break;
default:
token = 'TOKEN_DEV';
}
analytics.load(token);