Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ The projects that can be added to a Monux monorepo also provide a lot of functio
- [Adding a new project to the monorepo](#adding-a-new-project-to-the-monorepo)
- [Running development services](#running-development-services)
- [Listing monorepo services](#listing-monorepo-services)
- [Stopping development services](#stopping-development-services)
- [Running npm scripts](#running-npm-scripts)
- [Running npm scripts in multiple projects](#running-npm-scripts-in-multiple-projects)
- [Handling environment variables](#handling-environment-variables)
Expand All @@ -55,6 +56,7 @@ The projects that can be added to a Monux monorepo also provide a lot of functio
- [How do they work?](#how-do-they-work)
- [Starting prod locally](#starting-prod-locally)
- [Starting in production](#starting-in-production)
- [Global Management of Monorepos](#global-management-of-monorepos)
- [Supported project types](#supported-project-types)
- [Angular app](#angular-app)
- [Angular website](#angular-website)
Expand Down Expand Up @@ -107,13 +109,22 @@ That section also includes a guide on how to add projects manually.

## Running development services
Some things like databases will be added to the monorepo solely in the docker compose.<br>
To use these during development, the cli includes the `mx up-dev` command.
To use these during development, the cli includes the `mx up` command, where you can provide the "dev" environment when prompted.

## Listing monorepo services
To list all of Monux monorepos and their respective docker services we included the `mx ls` and `mx la` commands.

Where `ls` or `list` only shows monorepos with currently running docker services, while `la` or `list-all` also shows monorepos with stopped docker services.

Both of these commands finds Monux workspaces globally. The only constraint for being found is that `mx up` had to be used at least once.

## Stopping development services
To stop your services again, Monux provides the `mx down` command.

>If you are used to docker:<br>
>Please note that this is NOT equivalent to `docker compose down`.
>This actually uses `docker compose stop` internally.

## Running npm scripts
To run an npm script of one of your projects you can use `mx {projectName} {npmScript}`. This works for projects in the "apps" and "libs" directories of your monorepo.

Expand All @@ -135,7 +146,7 @@ How these environment files are generated depends on a `environment.model.ts`-fi
That way it is possible to only have certain variables like an contact email-address be available to a website project, while certain other variables like a db-password are not.

### Calculated environment variables
Working with static variables can sometimes be pretty tedious. This is especially true when you want to support different "modes" in which to launch your application (like we do with the `mx up`, `mx up-dev` or `mx up-local` commands).<br>
Working with static variables can sometimes be pretty tedious. This is especially true when you want to support different "modes" in which to launch your application (like we do with the different options for `mx up`).<br>
For example, if we defined the variables "api_base_url", "website_base_url" and "admin_base_url" all statically, we would need to manually fiddle with the `.env`-file anytime we switch between dev and local.<br>
To solve this, Monux implements calculated environment variables.

Expand Down Expand Up @@ -240,7 +251,7 @@ Notice that these files also include a Environment type for the specific project

#### Automatically create the projects environment.ts files
The `environment.ts` that is used by the project needs to be generated by running `mx prepare`. This will also take care of the validation mentioned before.<br>
When you use any of `mx up`, `mx up-dev` or `mx up-local` to deploy the project, the `mx prepare` command is actually called internally, so you don't have to call it manually.
When you use `mx up` to deploy the project, the `mx prepare` command is actually called internally, so you don't have to call it manually.

## Handling initial database content
Because Docker Compose environment variables can often only initialize 1 default user and 1 default db, Monux provides a way to add multiple of these during the `mx prepare` command.
Expand Down Expand Up @@ -268,15 +279,23 @@ Monux handles everything regarding mapping these variable names back to values a
## Starting prod locally
Often times you want to test your project under production like constraints (eg. when developing a website to check its SEO performance).

For that Monux provides the command `mx up-local`.
For that, you can run `mx up` with the environment "local".

## Starting in production
You can start the whole monorepo with the single command `mx up`.
You can start the whole monorepo with running `mx up` and then selecting "prod" as the environment.

This will try to run the `mx prepare` command.<br>
The only info required by that command is inside the `.env`-file.<br>
Monux also validates the `.env`-file, so by continueously running the command you can fill it little by little and don't need to worry that you start your monorepo with invalid or missing environment variables.

## Global Management of Monorepos
The Monux cli commands `mx up`, `mx down`, `mx ls` and `mx la` can be run globally. That way you don't have to open up a certain directory just to exit some services.

The way this works is by supplying an additional argument to `up` and `down`: The name of the monorepo.

The name of the monorepo is simply the directory name. If you are unsure about that or don't quite now what is currently running, you can simply use the `mx ls`/`mx la` commands.<br>
They will give you a nice overview of all monorepos on your machine and their respective services, including if they are running or not.

# Supported project types
## Angular app
## Angular website
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "monux-cli",
"version": "2.1.2",
"version": "2.2.0",
"license": "MIT",
"main": "index.js",
"engines": {
Expand Down
10 changes: 6 additions & 4 deletions src/angular/angular.utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,15 @@ export abstract class AngularUtilities {
name,
getPath(projectRoot, 'src', 'environment', ENVIRONMENT_MODEL_TS_FILE_NAME),
DefaultEnvKeys.baseUrl(apiName),
false
false,
getPath('.')
);
await EnvUtilities.addProjectVariableKey(
name,
getPath(projectRoot, 'src', 'environment', ENVIRONMENT_MODEL_TS_FILE_NAME),
DefaultEnvKeys.domain(apiName),
false
false,
getPath('.')
);
const authServicePath: string = getPath(projectRoot, 'src', 'app', 'services', 'auth.service.ts');
await FsUtilities.createFile(authServicePath, authServiceContent);
Expand Down Expand Up @@ -619,8 +621,8 @@ export abstract class AngularUtilities {
* @param domain - The domain of the project. Is needed to create the robots.txt file when the baseUrl environment variable has not been set yet.
*/
static async addSitemapAndRobots(root: string, projectName: string, domain: string): Promise<void> {
const app: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(projectName);
await RobotsUtilities.createRobotsTxtForApp(app, 'dev.docker-compose.yaml', domain);
const app: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(projectName, getPath('.'));
await RobotsUtilities.createRobotsTxtForApp(app, 'dev.docker-compose.yaml', domain, getPath('.'));
await FsUtilities.createFile(getPath(root, 'src', SITEMAP_FILE_NAME), [
'<?xml version="1.0" encoding="UTF-8"?>',
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { TsConfig, TsConfigUtilities } from '../../../tsconfig';
import { OmitStrict } from '../../../types';
import { getPath, mergeDeep } from '../../../utilities';
import { WorkspaceConfig, WorkspaceProject, WorkspaceUtilities } from '../../../workspace';
import { AddCommand } from '../models';
import { BaseAddCommand } from '../models';
import { AddConfiguration } from '../models/add-configuration.model';

/**
Expand All @@ -33,7 +33,7 @@ type CreateResult = {
/**
* Command that handles adding an angular library to the monorepo.
*/
export class AddAngularLibraryCommand extends AddCommand<AddAngularLibraryConfiguration> {
export class AddAngularLibraryCommand extends BaseAddCommand<AddAngularLibraryConfiguration> {

protected override configQuestions: QuestionsFor<OmitStrict<AddAngularLibraryConfiguration, keyof AddConfiguration>> = {
scope: {
Expand Down Expand Up @@ -118,7 +118,7 @@ export class AddAngularLibraryCommand extends AddCommand<AddAngularLibraryConfig
[PACKAGE_JSON_FILE_NAME]
);

const newProject: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name);
const newProject: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name, getPath('.'));
return { root: newProject.path, oldPackageJson };
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { TsConfig, TsConfigUtilities } from '../../../tsconfig';
import { OmitStrict } from '../../../types';
import { getPath, toPascalCase } from '../../../utilities';
import { WorkspaceProject, WorkspaceUtilities } from '../../../workspace';
import { AddCommand } from '../models';
import { BaseAddCommand } from '../models';
import { AddConfiguration } from '../models/add-configuration.model';

/**
Expand Down Expand Up @@ -41,7 +41,7 @@ type AddAngularWebsiteConfiguration = AddConfiguration & {
/**
* Command that handles adding an angular website to the monorepo.
*/
export class AddAngularWebsiteCommand extends AddCommand<AddAngularWebsiteConfiguration> {
export class AddAngularWebsiteCommand extends BaseAddCommand<AddAngularWebsiteConfiguration> {

protected override configQuestions: QuestionsFor<OmitStrict<AddAngularWebsiteConfiguration, keyof AddConfiguration>> = {
port: {
Expand Down Expand Up @@ -75,7 +75,8 @@ export class AddAngularWebsiteCommand extends AddCommand<AddAngularWebsiteConfig

const prodRootDomain: string = await EnvUtilities.getEnvVariable(
DefaultEnvKeys.PROD_ROOT_DOMAIN,
'dev.docker-compose.yaml'
'dev.docker-compose.yaml',
getPath('.')
);
const domain: string = config.subDomain ? `${config.subDomain}.${prodRootDomain}` : prodRootDomain;

Expand Down Expand Up @@ -111,8 +112,8 @@ export class AddAngularWebsiteCommand extends AddCommand<AddAngularWebsiteConfig
await AngularUtilities.setupTracking(config.name);
}
await NpmUtilities.updatePackageJson(config.name, { scripts: { start: `ng serve --port ${config.port}` } });
const app: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name);
await EnvUtilities.buildEnvironmentFileForApp(app, true, 'dev.docker-compose.yaml');
const app: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name, getPath('.'));
await EnvUtilities.buildEnvironmentFileForApp(app, true, 'dev.docker-compose.yaml', getPath('.'));
}

private async createDefaultPages(root: string, titleSuffix: string, domain: string): Promise<void> {
Expand Down Expand Up @@ -236,7 +237,7 @@ export class AddAngularWebsiteCommand extends AddCommand<AddAngularWebsiteConfig
`new ${config.name}`,
{ '--skip-git': true, '--style': 'css', '--inline-style': true, '--ssr': true }
);
const newProject: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name);
const newProject: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name, getPath('.'));
await FsUtilities.updateFile(getPath(newProject.path, 'src', 'app', 'app.component.html'), '', 'replace');
await AngularUtilities.addProvider(newProject.path, 'provideHttpClient(withInterceptorsFromDi(), withFetch())', [
// eslint-disable-next-line sonar/no-duplicate-string
Expand Down
17 changes: 10 additions & 7 deletions src/commands/add/add-angular/add-angular.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import { TsConfig, TsConfigUtilities } from '../../../tsconfig';
import { OmitStrict } from '../../../types';
import { getPath, toPascalCase } from '../../../utilities';
import { WorkspaceProject, WorkspaceUtilities } from '../../../workspace';
import { AddCommand } from '../models/add-command.class';
import { AddConfiguration } from '../models/add-configuration.model';
import { BaseAddCommand, AddConfiguration } from '../models';

/**
* Configuration for adding a new angular app.
Expand Down Expand Up @@ -42,7 +41,7 @@ type AddAngularConfiguration = AddConfiguration & {
/**
* Command that handles adding an angular application to the monorepo.
*/
export class AddAngularCommand extends AddCommand<AddAngularConfiguration> {
export class AddAngularCommand extends BaseAddCommand<AddAngularConfiguration> {
protected override readonly configQuestions: QuestionsFor<OmitStrict<AddAngularConfiguration, keyof AddConfiguration>> = {
port: {
type: 'number',
Expand Down Expand Up @@ -100,7 +99,11 @@ export class AddAngularCommand extends AddCommand<AddAngularConfiguration> {
AngularUtilities.setupMaterial(root)
]);

const prodRootDomain: string = await EnvUtilities.getEnvVariable(DefaultEnvKeys.PROD_ROOT_DOMAIN, 'dev.docker-compose.yaml');
const prodRootDomain: string = await EnvUtilities.getEnvVariable(
DefaultEnvKeys.PROD_ROOT_DOMAIN,
'dev.docker-compose.yaml',
getPath('.')
);
const fullDomain: string = config.subDomain ? `${config.subDomain}.${prodRootDomain}` : prodRootDomain;

await AngularUtilities.setupNavigation(root, config.name);
Expand All @@ -119,8 +122,8 @@ export class AddAngularCommand extends AddCommand<AddAngularConfiguration> {

await NpmUtilities.updatePackageJson(config.name, { scripts: { start: `ng serve --port ${config.port}` } });

const app: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name);
await EnvUtilities.buildEnvironmentFileForApp(app, false, 'dev.docker-compose.yaml');
const app: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name, getPath('.'));
await EnvUtilities.buildEnvironmentFileForApp(app, false, 'dev.docker-compose.yaml', getPath('.'));
}

private async setupTailwind(root: string): Promise<void> {
Expand Down Expand Up @@ -189,7 +192,7 @@ export class AddAngularCommand extends AddCommand<AddAngularConfiguration> {
`new ${config.name}`,
{ '--skip-git': true, '--style': 'css', '--inline-style': true, '--ssr': true }
);
const newProject: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name);
const newProject: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name, getPath('.'));
await FsUtilities.updateFile(getPath(newProject.path, 'src', 'app', 'app.component.html'), '', 'replace');
await AngularUtilities.addProvider(newProject.path, 'provideHttpClient(withInterceptorsFromDi(), withFetch())', [
// eslint-disable-next-line sonar/no-duplicate-string
Expand Down
31 changes: 21 additions & 10 deletions src/commands/add/add-loopback/add-loopback.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { TsConfigUtilities } from '../../../tsconfig';
import { OmitStrict } from '../../../types';
import { getPath, toKebabCase, toPascalCase } from '../../../utilities';
import { WorkspaceProject, WorkspaceUtilities } from '../../../workspace';
import { AddCommand } from '../models';
import { BaseAddCommand } from '../models';
import { AddConfiguration } from '../models/add-configuration.model';

/**
Expand Down Expand Up @@ -46,7 +46,7 @@ export type AddLoopbackConfiguration = AddConfiguration & {
/**
* Command that handles adding a loopback api to the monorepo.
*/
export class AddLoopbackCommand extends AddCommand<AddLoopbackConfiguration> {
export class AddLoopbackCommand extends BaseAddCommand<AddLoopbackConfiguration> {
protected override configQuestions: QuestionsFor<OmitStrict<AddLoopbackConfiguration, keyof AddConfiguration>> = {
port: {
type: 'number',
Expand Down Expand Up @@ -80,7 +80,7 @@ export class AddLoopbackCommand extends AddCommand<AddLoopbackConfiguration> {

override async run(): Promise<void> {
const config: AddLoopbackConfiguration = await this.getConfig();
const { dbServiceName, databaseName } = await DbUtilities.configureDb(config.name);
const { dbServiceName, databaseName } = await DbUtilities.configureDb(config.name, undefined, getPath('.'));
const root: string = await this.createProject(config);
await EnvUtilities.setupProjectEnvironment(root, false);
await this.createLoopbackDatasource(dbServiceName, databaseName, root, config.name);
Expand Down Expand Up @@ -119,8 +119,8 @@ export class AddLoopbackCommand extends AddCommand<AddLoopbackConfiguration> {
await LoopbackUtilities.setupChangeSets(root, config.name);
await LoopbackUtilities.setupMigrations(root, config.name);

const app: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name);
await EnvUtilities.buildEnvironmentFileForApp(app, false, 'dev.docker-compose.yaml');
const app: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name, getPath('.'));
await EnvUtilities.buildEnvironmentFileForApp(app, false, 'dev.docker-compose.yaml', getPath('.'));
}

private async updateDockerFile(root: string): Promise<void> {
Expand Down Expand Up @@ -221,11 +221,22 @@ export class AddLoopbackCommand extends AddCommand<AddLoopbackConfiguration> {
projectName,
environmentModel,
DefaultEnvKeys.dbPassword(dbServiceName, databaseName),
true
true,
getPath('.')
);
await EnvUtilities.addProjectVariableKey(projectName, environmentModel, DefaultEnvKeys.dbUser(dbServiceName, databaseName), true);
await EnvUtilities.addProjectVariableKey(projectName, environmentModel, DefaultEnvKeys.dbName(dbServiceName, databaseName), true);
await EnvUtilities.addProjectVariableKey(projectName, environmentModel, DefaultEnvKeys.dbHost(dbServiceName), true);
await EnvUtilities.addProjectVariableKey(
projectName,
environmentModel,
DefaultEnvKeys.dbUser(dbServiceName, databaseName),
true,
getPath('.')
);
await EnvUtilities.addProjectVariableKey(projectName,
environmentModel,
DefaultEnvKeys.dbName(dbServiceName, databaseName),
true,
getPath('.'));
await EnvUtilities.addProjectVariableKey(projectName, environmentModel, DefaultEnvKeys.dbHost(dbServiceName), true, getPath('.'));
}

private async createProject(config: AddLoopbackConfiguration): Promise<string> {
Expand All @@ -240,7 +251,7 @@ export class AddLoopbackCommand extends AddCommand<AddLoopbackConfiguration> {
vscode: false
}
});
const newProject: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name);
const newProject: WorkspaceProject = await WorkspaceUtilities.findProjectOrFail(config.name, getPath('.'));
await Promise.all([
FsUtilities.rm(getPath(newProject.path, 'src', '__tests__')),
FsUtilities.rm(getPath(newProject.path, GIT_IGNORE_FILE_NAME)),
Expand Down
Loading