Skip to content

Commit e372a0c

Browse files
authored
Merge pull request #125 from microservices-suite/repo-engineering/universal-cli
Repo engineering/universal cli
2 parents eeda746 + 63ecb15 commit e372a0c

File tree

11 files changed

+187
-60
lines changed

11 files changed

+187
-60
lines changed

.suite-cli/cli/cli.js

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
#!/usr/bin/env node
22

3+
const ora = require('ora')
34
const { Command } = require('commander');
45
const { createPromptModule } = require('inquirer');
5-
const figlet = require('figlet');
6+
const { execSync } = require('node:child_process')
67
const actionHandlers = require('./scripts')
78
const { logInfo } = require('./scripts/scripts.module');
89
const program = new Command()
@@ -78,7 +79,36 @@ program
7879
program
7980
.command('generate')
8081
.description('Generate a new monorepo resource')
81-
.action(() => {
82+
.action(async () => {
83+
try {
84+
// Check if Yarn is installed
85+
execSync('yarn --version', { stdio: 'ignore' });
86+
} catch (error) {
87+
const spinner = ora().fail('Yarn is not installed.');
88+
89+
const { installYarn } = await prompt([
90+
{
91+
type: 'confirm',
92+
name: 'installYarn',
93+
message: 'Would you like to install it now?',
94+
default: true
95+
}
96+
]);
97+
98+
if (installYarn) {
99+
try {
100+
spinner.start('Installing Yarn...');
101+
execSync('npm install -g yarn', { stdio: 'inherit' });
102+
spinner.succeed('Yarn installed successfully.');
103+
} catch (installError) {
104+
spinner.fail('Failed to install Yarn. Please install it manually and try again.');
105+
process.exit(1);
106+
}
107+
} else {
108+
spinner.fail('Yarn is required to proceed. Exiting.');
109+
process.exit(1);
110+
}
111+
}
82112
prompt([
83113
{
84114
type: 'list',
@@ -120,13 +150,13 @@ program
120150
message: 'Select license:',
121151
choices: ['ISC', 'MIT'],
122152
default: 'ISC'
123-
},{
153+
}, {
124154
type: 'input',
125155
name: 'service_name',
126156
message: 'Initial service:',
127157
default: 'microservice1'
128158
}
129-
159+
130160
])
131161
.then(answers => {
132162
// find out if this separater works on windows

.suite-cli/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@microservices-suite/cli",
3-
"version": "2.0.3",
3+
"version": "2.0.4",
44
"description": "This is the CLI tool for running the microservices-suite monorepo. It contains functionalities and tools required for automation and managing the repo across supported platforms. Works on Windows,MacOS and Linux as well as support to some extend other variants like SunOS, IBM AIX, FreeBSD, OpenBSD and more",
55
"main": "cli.js",
66
"repository": "https://github.com/microservices-suite/node-microservices-suite.git",
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module.exports = ({ services }) => `
2+
${services.map(({ service, port }) => `
3+
upstream ${service} {
4+
server ${service}:${port};
5+
}`).join('')}
6+
7+
server {
8+
listen 80;
9+
10+
${services.map(({ service }) => `
11+
location /${service} {
12+
rewrite /${service}/(.*) /$1 break;
13+
proxy_pass http://${service};
14+
}`).join('')}
15+
}
16+
`.trim();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module.exports = ({ services }) => `
2+
version: '3.8'
3+
services:
4+
mongodb:
5+
image: mongo:latest
6+
container_name: mongodb
7+
ports:
8+
- '27017:27017'
9+
rabbitmq:
10+
image: rabbitmq:3.8
11+
container_name: rabbitmq
12+
ports:
13+
- '5672:5672'
14+
- '15672:15672'
15+
${services.map(({ service, ports, prerequisites }) => `
16+
${service}:
17+
depends_on:
18+
${prerequisites.map((prerequisite) => ` - ${prerequisite}`).join('\n')}
19+
container_name: ${service}
20+
restart: always
21+
build:
22+
context: ../../../microservices/${service}
23+
dockerfile: DockerFile.dev
24+
ports:
25+
${ports.map((p) => ` - '${p}:${p}'`).join('\n')}
26+
volumes:
27+
- /app/node_modules
28+
- ../../../microservices/${service}:/app`).join('\n')}
29+
`.trim();
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = () =>`
2+
FROM nginx
3+
COPY ./default.conf /etc/nginx/conf.d/default.conf`

.suite-cli/cli/scripts/assets/serverContent.asset.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const app = express();
2121
app.use(express.json());
2222
2323
app.get('/', (req, res) => {
24-
res.json({ messae: 'hello from ${answers.project_base}/${answers.service_name}' });
24+
res.json({ message: 'hello from ${answers.project_base}/${answers.service_name}' });
2525
});
2626
2727
const server = http.createServer(app);

.suite-cli/cli/scripts/scripts.module.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -726,10 +726,10 @@ const resetRepo = ({ targetDir }) => {
726726
} else {
727727
// Unix-like commands
728728
commands = [
729-
`find ${targetDir} -type d -name 'node_modules' -exec rm -rf {} +`,
730-
`find ${targetDir} -type f -name 'package-lock.json' -delete`,
731-
`find ${targetDir} -type f -name 'yarn.lock' -delete`,
732-
`find ${targetDir} -type d -name 'yarn-*' -exec rm -rf {} +`
729+
`find ${targetDir} -maxdepth 3 -type d -name 'node_modules' -exec rm -rf {} +`,
730+
`find ${targetDir} -maxdepth 3 -type f -name 'package-lock.json' -delete`,
731+
`find ${targetDir} -maxdepth 3 -type f -name 'yarn.lock' -delete`,
732+
`find ${targetDir} -maxdepth 3 -type d -name 'yarn-*' -exec rm -rf {} +`
733733
];
734734
}
735735

@@ -849,6 +849,7 @@ const addPackageJson = async ({ project_root, answers }) => {
849849
// Add a package.json
850850
writeFileSync(join(project_root, 'package.json'), JSON.stringify(assets.rootPackageJsonContent({ answers, os }), null, 2));
851851

852+
// TODO: move these dependencies to assets
852853
const dependencies = [
853854
`${answers.project_base}/config@1.0.0`,
854855
`${answers.project_base}/errors@1.0.0`,
@@ -861,7 +862,7 @@ const addPackageJson = async ({ project_root, answers }) => {
861862
"winston",
862863
"mongoose"
863864
];
864-
const devDependencies = ["nodemon"];
865+
const devDependencies = ["nodemon","jest"];
865866
const configDependencies = ['dotenv', 'joi', 'morgan', 'winston']
866867
const utilitiesDependencies = ['joi']
867868
// Join dependencies into a single string for the command
@@ -920,14 +921,14 @@ const addPackageJson = async ({ project_root, answers }) => {
920921
childProcess.on('error', error => {
921922
spinner.fail('Failed to execute command');
922923
});
923-
924+
// TODO: check if yarn is installed first
924925
childProcess.on('exit', (code, signal) => {
925926
if (code !== 0) {
926927
spinner.fail('Command failed to complete successfully');
927928
return;
928929
}
929930
spinner.succeed('Dependencies installed successfully');
930-
spinner.info(`To start the project, run 'cd ${answers.repo_name} && yarn dev'`)
931+
spinner.info(`To start the project, run 'cd ${answers.repo_name} && suite start -v ${answers.service_name}'`)
931932
});
932933
}
933934

@@ -1060,7 +1061,6 @@ const addMicroservice = ({ project_root, answers }) => {
10601061
* @returns {void}
10611062
*/
10621063
const scaffoldNewRepo = async ({ answers }) => {
1063-
let project_root;
10641064
try {
10651065
project_root = generatRootPath({ currentDir: cwd() });
10661066
} catch (error) {
@@ -1220,16 +1220,15 @@ const releasePackage = async ({ package }) => {
12201220
const package_json_path = join(cwd(), 'package.json');
12211221

12221222
// Read the package.json file
1223-
console.log({ package_json_path })
12241223
const { workspace_name } = retrieveWorkSpaceName({ package_json_path });
12251224
if (package) {
12261225
logInfo({ message: `Looking for package: ${workspace_name}/${package}` });
1227-
await executeCommand('yarn', ['workspace', `${workspace_name}/${package}`, 'release']);
1226+
await executeCommand('yarn', ['workspace', `${workspace_name}/${package}`, 'release'],{stdio:'inherit',shell:true});
12281227
} else {
1229-
await executeCommand('yarn', ['generate:release'], { cwd: cwd() });
1228+
await executeCommand('yarn', ['generate:release'], { cwd: cwd(),stdio:'inherit',shell:true });
12301229
}
12311230
} catch (error) {
1232-
console.error('Error occurred:', error);
1231+
ora().fail('Command failed to run');
12331232
}
12341233
}
12351234

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Changelog
22

33

4+
## v1.6.5
5+
6+
[compare changes](https://github.com/microservices-suite/node-microservices-suite/compare/v1.6.4...v1.6.5)
7+
8+
## v1.6.4
9+
10+
[compare changes](https://github.com/microservices-suite/node-microservices-suite/compare/v2.0.4...v1.6.4)
11+
412
## v1.6.3
513

614
[compare changes](https://github.com/microservices-suite/node-microservices-suite/compare/v2.0.2...v1.6.3)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@microservices-suite/microservices",
3-
"version": "1.6.3",
3+
"version": "1.6.5",
44
"description": "This is the central orchestration point for our microservices-based platform, encompassing user management, email communications, file upload handling, and role-based access control services. It serves as the backbone of our application's backend, facilitating seamless interaction, development, and scaling of individual microservices. The workspace is designed to streamline the development process, ensuring dependency consistency, and simplifying the build and deployment workflows across all services.\nThis root workspace enables our development teams to:\n- Manage dependencies for all services in a unified manner, reducing conflicts and easing updates.\n- Utilize shared configurations, such as ESLint rules, TypeScript configurations, and shared utility libraries, to maintain coding standards and reduce redundancy across services.\n- Implement global scripts for tasks like testing, linting, and deployment that can run across all services, ensuring consistency and efficiency in our development pipeline.\n- Leverage the workspace's structure for local development, allowing for easy testing of interactions between services without the need for deploying or configuring external environments.",
55
"keywords": [],
66
"author": "Gilbert Andanje",
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const fs = require('fs');
2+
const { retrieveWorkSpaceName } = require('./your-module-file');
3+
4+
// Mock the fs module to simulate file reading
5+
jest.mock('fs');
6+
7+
describe('retrieveWorkSpaceName', () => {
8+
test('should retrieve the workspace name from package.json', () => {
9+
// Mock the content of package.json
10+
const packageJsonContent = JSON.stringify({ name: 'your-workspace-name/workspace' });
11+
const packageJsonPath = '/path/to/package.json';
12+
13+
// Mock fs.readFileSync to return package.json content
14+
fs.readFileSync.mockReturnValue(packageJsonContent);
15+
16+
// Call the function with mocked parameters
17+
const result = retrieveWorkSpaceName({ package_json_path: packageJsonPath });
18+
19+
// Assert that the result matches the expected workspace name
20+
expect(result).toEqual({ workspace_name: 'your-workspace-name' });
21+
});
22+
23+
test('should throw an error when parsing package.json fails', () => {
24+
// Mock the content of package.json with invalid JSON
25+
const packageJsonContent = '{ invalid_json }';
26+
const packageJsonPath = '/path/to/package.json';
27+
28+
// Mock fs.readFileSync to return package.json content
29+
fs.readFileSync.mockReturnValue(packageJsonContent);
30+
31+
// Assert that the function throws an error
32+
expect(() => {
33+
retrieveWorkSpaceName({ package_json_path: packageJsonPath });
34+
}).toThrowError('Unexpected token i in JSON at position 2');
35+
// Adjust the error message to match the specific error thrown when parsing fails
36+
});
37+
38+
test('should handle missing package.json file', () => {
39+
// Mock fs.readFileSync to throw an error indicating that the file doesn't exist
40+
fs.readFileSync.mockImplementation(() => {
41+
throw new Error('ENOENT: no such file or directory');
42+
});
43+
44+
// Assert that the function throws an error
45+
expect(() => {
46+
retrieveWorkSpaceName({ package_json_path: '/nonexistent/path/to/package.json' });
47+
}).toThrowError('ENOENT: no such file or directory');
48+
});
49+
});

0 commit comments

Comments
 (0)