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
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ The projects that can be added to a Monux monorepo also provide a lot of functio
- [Initializing a new monorepo](#initializing-a-new-monorepo)
- [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)
- [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 Down Expand Up @@ -108,6 +109,11 @@ That section also includes a guide on how to add projects manually.
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.

## 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.

## 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 Down Expand Up @@ -140,7 +146,7 @@ But instead of parsing the values from the `.env`-file during the prepare step,
/**
* Defines how the CalculatedGlobalEnvironment values should be calculated.
* This is used by the "mx prepare" command.
* DONT CHANGE THE NAME ("calculationSchemaFor") OR FORMATTING. Otherwise Monux might not be able to detect it.
* DON'T CHANGE THE NAME ("calculationSchemaFor") OR FORMATTING. Otherwise Monux might not be able to detect it.
*/
const calculationSchemaFor: Record<
keyof CalculatedGlobalEnvironment,
Expand Down
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { configs } from 'eslint-config-service-soft';

// eslint-disable-next-line jsdoc/require-description
/** @type {import('eslint').Linter.Config} */
export default [
...configs,
Expand Down
2 changes: 2 additions & 0 deletions jest.config.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@

// eslint-disable-next-line jsdoc/require-description
/** @type {import('ts-jest').JestConfigWithTsJest} **/
const config = {
testEnvironment: 'node',
Expand Down
38 changes: 32 additions & 6 deletions package-lock.json

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

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "monux-cli",
"version": "2.1.0",
"version": "2.1.2",
"license": "MIT",
"main": "index.js",
"engines": {
Expand Down Expand Up @@ -40,6 +40,7 @@
},
"dependencies": {
"chalk": "^4.1.2",
"cli-table3": "^0.6.5",
"death": "^1.1.0",
"figlet": "^1.7.0",
"inquirer": "^10.2.2",
Expand All @@ -54,7 +55,7 @@
"@types/figlet": "^1.5.8",
"@types/js-yaml": "^4.0.9",
"eslint": "^9.24.0",
"eslint-config-service-soft": "^2.0.0",
"eslint-config-service-soft": "^2.0.8",
"jest": "^29.7.0",
"ngx-material-navigation": "^18.1.2",
"ts-jest": "^29.2.5"
Expand Down
2 changes: 2 additions & 0 deletions src/__testing__/mock/file-mock.utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { MockConstants, FileMockConstants, DirMockConstants } from './constants'
import { AngularJson } from '../../angular';
import { CPUtilities, FsUtilities, JsonUtilities } from '../../encapsulation';
import { EnvUtilities } from '../../env';
import { WorkspaceUtilities } from '../../workspace';

export abstract class FileMockUtilities {

Expand Down Expand Up @@ -38,6 +39,7 @@ export abstract class FileMockUtilities {
CPUtilities['cwd'] = mockConstants.PROJECT_DIR;
await this.mockFolders(mockConstants);
await this.mockFiles(filesToMock, contentOverrides, mockConstants);
await WorkspaceUtilities.createConfig();
}

private static async mockFolders(mockConstants: MockConstants): Promise<void> {
Expand Down
5 changes: 3 additions & 2 deletions src/angular/angular.utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,10 +616,11 @@ export abstract class AngularUtilities {
* Adds a sitemap.xml and a robots.txt to a project at the given path.
* @param root - The root of the angular project to add the files to.
* @param projectName - The name of the project.
* @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): Promise<void> {
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');
await RobotsUtilities.createRobotsTxtForApp(app, 'dev.docker-compose.yaml', domain);
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
52 changes: 25 additions & 27 deletions src/commands/add/add-angular-website/add-angular-website.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,35 +79,33 @@ export class AddAngularWebsiteCommand extends AddCommand<AddAngularWebsiteConfig
);
const domain: string = config.subDomain ? `${config.subDomain}.${prodRootDomain}` : prodRootDomain;

await AngularUtilities.addSitemapAndRobots(root, config.name);
await AngularUtilities.addSitemapAndRobots(root, config.name, domain);

await Promise.all([
this.cleanUp(root),
this.setupTsConfig(root, config.name),
this.createDockerfile(root, config),
AngularUtilities.setupNavigation(root, config.name),
EslintUtilities.setupProjectEslint(root, true),
this.setupTailwind(root),
DockerUtilities.addServiceToCompose(
{
name: config.name,
build: {
dockerfile: `./${root}/${DOCKER_FILE_NAME}`,
context: '.'
},
volumes: [{ path: `/${config.name}` }]
await this.cleanUp(root);
await this.setupTsConfig(root, config.name);
await this.createDockerfile(root, config);
await AngularUtilities.setupNavigation(root, config.name);
await EslintUtilities.setupProjectEslint(root, true);
await this.setupTailwind(root);
await DockerUtilities.addServiceToCompose(
{
name: config.name,
build: {
dockerfile: `./${root}/${DOCKER_FILE_NAME}`,
context: '.'
},
4000,
true,
config.subDomain
),
AngularUtilities.updateAngularJson(
getPath(root, ANGULAR_JSON_FILE_NAME),
{ $schema: '../../node_modules/@angular/cli/lib/config/schema.json' }
),
AngularUtilities.setupMaterial(root),
EnvUtilities.setupProjectEnvironment(root, false)
]);
volumes: [{ path: `/${config.name}` }]
},
4000,
true,
config.subDomain
);
await AngularUtilities.updateAngularJson(
getPath(root, ANGULAR_JSON_FILE_NAME),
{ $schema: '../../node_modules/@angular/cli/lib/config/schema.json' }
);
await AngularUtilities.setupMaterial(root);
await EnvUtilities.setupProjectEnvironment(root, false);
await this.createDefaultPages(root, config.titleSuffix, domain);
if (config.addTracking) {
await AngularUtilities.setupTracking(config.name);
Expand Down
6 changes: 5 additions & 1 deletion src/commands/command.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,9 @@ export enum Command {
GP = 'gp',
RUN_ALL = 'run-all',
RUN_MANY = 'run-many',
RA = 'ra'
RA = 'ra',
LIST = 'list',
LS = 'ls',
LIST_ALL = 'list-all',
LA = 'la'
}
2 changes: 1 addition & 1 deletion src/commands/down-dev/down-dev.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import { CPUtilities } from '../../encapsulation';
* Shuts down the dev services.
*/
export function runDownDev(): void {
CPUtilities.execSync(`docker compose -f ${DEV_DOCKER_COMPOSE_FILE_NAME} down`);
CPUtilities.execSync(`docker compose -f ${DEV_DOCKER_COMPOSE_FILE_NAME} stop`);
}
2 changes: 1 addition & 1 deletion src/commands/down-local/down-local.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import { CPUtilities } from '../../encapsulation';
* Shuts down the local services.
*/
export function runDownLocal(): void {
CPUtilities.execSync(`docker compose -f ${LOCAL_DOCKER_COMPOSE_FILE_NAME} down`);
CPUtilities.execSync(`docker compose -f ${LOCAL_DOCKER_COMPOSE_FILE_NAME} stop`);
}
2 changes: 1 addition & 1 deletion src/commands/down/down.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import { CPUtilities } from '../../encapsulation';
* Shuts down the monorepo.
*/
export function runDown(): void {
CPUtilities.execSync('docker compose down');
CPUtilities.execSync('docker compose stop');
}
2 changes: 2 additions & 0 deletions src/commands/help/help.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export function runHelp(): void {
`deploys the monorepo. This includes the ${ChalkUtilities.secondary(Command.PREPARE)} command.`
);
console.log(getCommandLabel(Command.DOWN, Command.D), 'stops the currently deployed monorepo');
console.log(getCommandLabel(Command.LIST, Command.LS), 'lists running monorepos with their respective docker services');
console.log(getCommandLabel(Command.LIST_ALL, Command.LA), 'lists all monorepos with their respective docker services');
console.log(getCommandLabel(Command.GENERATE_PAGE, Command.GP), 'generates a new page for an angular project');
console.log();
console.log(`${ChalkUtilities.boldUnderline('Running an npm script')}:`);
Expand Down
3 changes: 2 additions & 1 deletion src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export * from './up-local';
export * from './is-command.function';
export * from './is-error-with-signal.function';
export * from './is-exit-prompt-error.function';
export * from './exit-with-interrupt.function';
export * from './exit-with-interrupt.function';
export * from './list';
3 changes: 2 additions & 1 deletion src/commands/init/init.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { WorkspaceUtilities } from '../../workspace';
import { exitWithError } from '../exit-with-error.function';
import { InitConfiguration } from './init-configuration.model';
import { GithubUtilities } from '../../github';
import { getPath } from '../../utilities';

const initConfigQuestions: QuestionsFor<InitConfiguration> = {
prodRootDomain: {
Expand All @@ -33,7 +34,7 @@ const initConfigQuestions: QuestionsFor<InitConfiguration> = {
* Runs the init cli command.
*/
export async function runInit(): Promise<void> {
if (await FsUtilities.exists(WORKSPACE_FILE_NAME)) {
if (await FsUtilities.exists(getPath(WORKSPACE_FILE_NAME))) {
exitWithError('Error: The current directory is already a monorepo workspace');
}

Expand Down
67 changes: 67 additions & 0 deletions src/commands/list/get-docker-services.function.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { execSync } from 'child_process';

import { FullyParsedDockerService, StringifiedDockerServiceWithParsedLabels, isFullyParsedDockerService, StringifiedDockerService } from './stringified-docker-service.model';
import { WORKSPACE_FILE_NAME } from '../../constants';
import { DockerLabel } from '../../docker';
import { JsonUtilities } from '../../encapsulation';
import { getPath } from '../../utilities';
import { WorkspaceConfig, WorkspaceUtilities } from '../../workspace';

/**
* Gets all docker services that come from a project with an mx.workspace.json.
* @param all - Whether or not to get all docker services.
* @returns An Array of grouped docker services.
*/
export async function getDockerServices(all: boolean): Promise<FullyParsedDockerService[]> {
const output: string = execSync(`docker ps${all ? ' -a' : ''} --format "{{json .}}" --size=false`)
.toString()
.trim();

// Split the output into lines and parse each JSON line.
const services: StringifiedDockerServiceWithParsedLabels[] = await Promise.all(output
.split('\n')
.filter(line => line)
.map(async line => {
const service: StringifiedDockerService = JsonUtilities.parse(line);
const labels: Record<string, string | undefined> = parseLabels(service.Labels);
const config: WorkspaceConfig | undefined = await getWorkspaceConfig(labels);
return {
...service,
Labels: labels,
config: config
};
}));

return services.filter(isFullyParsedDockerService);
}

/**
* Gets the workspace project for a docker service with the given labels.
* @param labels - The docker labels of the service to get the workspace config for.
* @returns The found workspace config or undefined.
*/
async function getWorkspaceConfig(labels: Record<DockerLabel, string | undefined>): Promise<WorkspaceConfig | undefined> {
const composeProjectDir: string | undefined = labels[DockerLabel.COMPOSE_PROJECT_DIR];
if (!composeProjectDir) {
return undefined;
}
const config: WorkspaceConfig | undefined = await WorkspaceUtilities.getConfig(getPath(composeProjectDir, WORKSPACE_FILE_NAME));
return config;
}

/**
* Parses the labels of a docker service.
* @param labelStr - The raw labels string value.
* @returns A record with the label as key and the label value.
*/
function parseLabels(labelStr: string): Record<string, string | undefined> {
return labelStr.split(',')
.reduce<Record<string, string | undefined>>((acc, pair) => {
if (!pair) {
return acc;
}
const [key, ...value] = pair.split('=');
acc[key.trim()] = value.join('').trim();
return acc;
}, {});
}
1 change: 1 addition & 0 deletions src/commands/list/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './list.command';
Loading
Loading