Sequelize migrations - run automatically when node application starts - javascript

I am trying to get my Sequelize migration scripts to run automatically when my node application starts. I have manually tested the migration scripts to make sure they are running correctly, by running db:migrate command.
Now, I have added this file to run the migration scripts:
index.js
const {exec} = require('child_process');
const Sequelize = require('sequelize');
const config = require("config");
const sequelize = new Sequelize(config.get('postgres'));
async function start() {
await new Promise((resolve, reject) => {
const migrate = exec(
'npm run db:migrate',
{env: 'development'},
(err, stdout, stderr) => {
if (err) {
reject(err);
} else {
resolve();
}
}
);
// Forward stdout+stderr to this process
migrate.stdout.pipe(process.stdout);
migrate.stderr.pipe(process.stderr);
});
}
module.exports = {
start: start
};
And in server.js:
async function start(appStarted) {
logger.info('Initializing ...');
// execute pending migrations
logger.info('Migrating DB...');
await require('../migrations').start();
logger.info('DB Migration complete.');
When I start the app, it displays Migrating DB... and gets stuck there.
How can I resolve this?

You can listen for the console message and kill the child process, like this:
// Listen for the console.log message and kill the process to proceed to the next step in the npm script
migrate.stdout.on('data', (data) => {
console.log(data);
if (data.indexOf('No migrations were executed, database schema was already up to date.') !== -1) {
migrate.kill();
}
});
This will make sure that the child process is killed when you've already run your migrations.

Related

discord.js says data isn't defined even though it is

I'm making a discord bot and I am following a guide, the following code is copied from the guide and is for registering slash commands
const { REST } = require('#discordjs/rest');
const { Routes } = require('discord-api-types/v10');
const { clientId, token } = require('./config.json');
const fs = require('node:fs');
const commands = [];
// Grab all the command files from the commands directory you created earlier
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
commands.push(command.data.toJSON());
}
// Construct and prepare an instance of the REST module
const rest = new REST({ version: '10' }).setToken(token);
// and deploy your commands!
(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);
// The put method is used to fully refresh all commands in the guild with the current set
await rest.put(
Routes.applicationCommands(clientId),
{ body: commands },
);
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
// And of course, make sure you catch and log any errors!
console.error(error);
}
})();
The commands folder only has 1 file right now and it is ping.js: (also copied from the guide)
const { SlashCommandBuilder } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with Pong!'),
async execute(interaction) {
await interaction.reply('Pong!');
},
};
This code worked before, I tried it and it worked fine even with 2 commands. But when I tried adding a third one (by copying ping and just changing the values) it started saying: "ReferenceError: data is not defined" whenever I tried to run it. So I deleted the file and tried running it with the 2 that already worked but now it gave this error with those 2 as well. So I tried only with the ping file from the guide itself and even tried copying from the guide again and I couldn't get it to work again.

How to setup Unit test with mocha and mongodb-memory-server?

I am trying to make test case suite with mocha and mongodb-memory-server(as an in memory db). I am trying to implement this in the way below.
Project structure:
test runner: (in package.json)
"scripts": {
"test": "mocha 'app/**/*.spec.js' --recursive --exit"
},
So, First I need to initialise the in memory MongoDB, thats why I am using global.spec.js which looks like this,
const { MongoMemoryServer } = require("mongodb-memory-server");
const mongoose = require("mongoose");
mongoose.set("usePushEach", true);
let mongoServer;
before(async function () {
// mongod donwload on first time
this.timeout(30 * 1000);
mongoServer = new MongoMemoryServer();
const mongoUri = await mongoServer.getUri();
await mongoose.connect(mongoUri, {});
process.env.AGENDA_DB_URI = mongoUri;
});
after(function () {
mongoose.disconnect();
mongoServer.stop();
});
and a test-setup.js file which looks like this,
const { MongoMemoryServer } = require("mongodb-memory-server");
(async function() {
// trigger downloading mongodb executable on first time
const mongoServer = new MongoMemoryServer();
await mongoServer.getUri();
mongoServer.stop();
})()
.then(() => {
process.exit(0);
})
.catch(err => {
console.error(err);
process.exit(1);
});
all *.spec.js files will be inside modules folder. In simple word each folder inside module will have one .spec.js file. If I try to run this using the npm run test command it is throwing me some error, that looks like this,
1) "before all" hook
0 passing (448ms)
1 failing
1) "before all" hook:
Uncaught Error: TypeError: logWarnFn is not a function
I believe this logWarnFn error is coming from i18n. But when i start the server it is working fine.
versions:
"mongodb-memory-server": "^6.6.2",
"mocha": "^6.0.2",
"i18n": "0.8.3",

explorer.exe doesn't open correct folder when ran programmatically inside WSL

I'm trying to open explorer.exe from a Node.js script running inside WSL Ubuntu 20.04. The issue I've encountered is that explorer.exe never opens the folder I'd like it to. Instead of WSL user's home directory it opens my Windows user's Documents folder. What should I do to make explorer.exe open the folder I want?
Here's what I've tried:
The script first defines a function execShellCommand that promisifies exec. Then self-executing function first converts process.env.HOME to a Windows path with wslpath. Then it executes explorer.exe with the converted path as a parameter.
#!/usr/bin/node
const execShellCommand = async cmd => {
const exec = require('child_process').exec
return new Promise((resolve, reject) => {
exec(cmd, (error, stdout, stderr) => {
if (error) {
console.warn(error)
}
resolve(stderr ? stderr : stdout)
})
})
}
;(async () => {
const path = await execShellCommand(`wslpath -w "${process.env.HOME}"`)
console.log({ path })
await execShellCommand(`explorer.exe ${path}`)
})()
The output I get when I run my script in WSL
$ ./script.js
{ path: '\\\\wsl$\\Ubuntu-20.04\\home\\user\n' }
Error: Command failed: explorer.exe \\wsl$\Ubuntu-20.04\home\user
at ChildProcess.exithandler (child_process.js:308:12)
at ChildProcess.emit (events.js:315:20)
at maybeClose (internal/child_process.js:1048:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:288:5) {
killed: false,
code: 1,
signal: null,
cmd: 'explorer.exe \\\\wsl$\\Ubuntu-20.04\\home\\user\n'
}
explorer.exe does run regardless of the error shown in the output. The weird part is that if I run the same command my script tries to run (explorer.exe \\\\wsl$\\Ubuntu-20.04\\home\\user\n) directly in WSL terminal explorer.exe does open the folder I want it to. Trimming the new line at the end of the path doesn't help.
I think you have to do some additional escaping on the backslashes that are produced by wslpath. The code below works for me, meaning it opens the correct directory in Windows Explorer.
Note: it does still throw the error you mentioned, which I think is due to the way node exits rather than anything wrong w/the execution of explorer.exe; I'm not a node expert by any stretch.
#!/usr/bin/node
const execShellCommand = async cmd => {
const exec = require('child_process').exec
return new Promise((resolve, reject) => {
exec(cmd, (error, stdout, stderr) => {
if (error) {
console.warn(error)
}
resolve(stderr ? stderr : stdout)
})
})
}
;(async () => {
let path = await execShellCommand(`wslpath -w "${process.env.HOME}"`)
console.log("before", {path});
path = path.replace(/\\/g,"\\\\");
console.log("after", {path});
await execShellCommand(`explorer.exe ${path}`)
})()
Even cleaner than replacing backslashes, I think this will work for you by resolving the $HOME variable directly into your command line:
await execShellCommand(`explorer.exe "$(wslpath -w $HOME)"`);

How to serve yarn build file with npm serve command?

Basically, my task is to compile a js file and serve it at http://localhost:5000/final.js
I have the following script.
Current issues
The console.log seems printing out of order.
Able to switch dir and run yarn build, but it seems not able to serve file
Here is the source code:
#!/usr/bin/env node
const frontendDir =
"/my/frontend";
const jsDir =
"/my/frontend/build/static/js";
// util
const util = require("util");
// exec
const exec = util.promisify(require("child_process").exec);
// async func
async function runcmd(cmd) {
try {
const { stdout, stderr } = await exec(cmd);
// need output
console.log("stdout:", stdout);
// need error
console.log("stderr:", stderr);
} catch (err) {
console.error(err);
}
}
try {
// go to front end dir
process.chdir(frontendDir);
console.log("Switch to dir: " + process.cwd());
// yarn build
runcmd("yarn build");
// go to file dir
process.chdir(jsDir);
console.log("Switch to dir: " + process.cwd());
// find that js file and copy it, rename it
runcmd("cp main.*.js final.js");
// serve at /my/frontend/build/static/js with url http://localhost:5000/final.js
runcmd("serve .");
} catch (err) {
console.log("chdir: " + err);
}
Here is the output from the script above
Switch to dir: /my/frontend
Switch to dir: /my/frontend/build/static/js
stdout:
stderr:
stdout: yarn run v1.21.1
$ react-app-rewired build && cpr ./build/ ../build/frontend/ -o
Creating an optimized production build...
Compiled successfully.
File sizes after gzip:
210.3 KB build/static/js/main.c1e6b0e9.js
The project was built assuming it is hosted at ./.
You can control this with the homepage field in your package.json.
The build folder is ready to be deployed.
Find out more about deployment here:
Done in 13.91s.
stderr:
// https://stackoverflow.com/questions/41037042/nodejs-wait-for-exec-in-function
const exec = require("child_process").exec;
function os_func() {
this.execCommand = function(cmd) {
return new Promise((resolve, reject) => {
exec(cmd, (error, stdout, stderr) => {
if (error) {
reject(error);
return;
}
resolve(stdout);
});
});
};
}
const os = new os_func();
process.chdir(frontendDir);
os.execCommand("yarn buildlocal")
.then(() => {
console.log("# done yarn build");
process.chdir(jsDir);
os.execCommand("cp main.*.js docman.js").then(() => {
console.log("# done copy and serve at port 5000");
os.execCommand("serve -l 5000 .").then(() => {});
});
})
.catch(err => {
console.log("os >>>", err);
});

Is there a way to check if an npm package needs a publish and only do a publish then?

I am creating a single code base containing multiple projects and each project can be published.
To create a streamlined continuous integration process I want my build agent to run a single command that publishes all the projects that need to be published. Obviously this doesnt work because a publish fails if it would publish over a previously published version and that causes the command to fail and all following commands do not execute. Something like this:
"build-package1": "ng build package1",
"publish-package1": "npm publish ./dist/package1",
"build-package2": "ng build package2",
"publish-package2": "npm publish ./dist/package2",
"build-all": "npm run build-package1 && npm run build-package2",
"publish-all": "npm run publish-package1 && npm run publish-package2"
So in this scenario if I run build-all, and then publish-all and package1 has not got a version change in it's package.json file then the command will fail and package2 will not be published.
Is there a command I can do that checks if a publish is needed before trying to publish?
I have seen this answer:
https://askubuntu.com/questions/334994/which-one-is-better-using-or-to-execute-multiple-commands-in-one-line/539293#539293
Ideally though I think finding out if a publish would work before attempting it might be cleaner.
It is possible to install npm locally to the project and then use imperative npm commands to check the latest published version, and the node file system to check local versions before using an imperative npm publish command
import * as fs from 'fs';
import * as npm from 'npm';
function getLocalPackageData(path): Promise<IPackageData> {
return new Promise((resolve, reject) => {
fs.readFile(path + '/package.json', (err, data) => {
if (err) reject(err);
const pData = JSON.parse(data as unknown as string);
resolve({ version: pData.version, name: pData.name });
});
});
}
function getPublishedPackageData(packageName: string) {
npm.commands.show([packageName], function (err, result) {
if (err) reject(err);
const latestVersion = Object.keys(result)[0];
resolve({ version: latestVersion, name: result[latestVersion].name });
});
}
function needsPublish(latestPackageSegments: IPackageVersionSegments, localPackageSegments: IPackageVersionSegments): boolean {
const majorGreater = localPackageSegments.major > latestPackageSegments.major;
const majorEqual = localPackageSegments.major === latestPackageSegments.major;
const minorGreater = localPackageSegments.minor > latestPackageSegments.minor;
const minorEqual = localPackageSegments.minor === latestPackageSegments.minor;
const bugFixGreater = localPackageSegments.bug > latestPackageSegments.bug;
return majorGreater ||
(majorEqual && (minorGreater || bugFixGreater)) ||
(majorEqual && minorEqual && bugFixGreater);
}
from there you can achieve checking local against published and do:
npm.commands.publish(projectPathsToUpdate, (error, res) => {
if (error) {
throw error;
}
console.log('================= published: =================');
packagesToUpdate.forEach(p => console.log(p.name + ' updated to ' + p.version));
});
hope this helps someone in the future - possibly me again.

Categories