RabbitPeepers supports The Armed Forces of UkraineDonate
RabbitPeepers supports The Armed Forces of UkraineDonate
Calculate project
Contact us
RabbitPeepers supports The Armed Forces of UkraineDonate
Calculate project

Get in touch to discuss your business idea

Let's Start

Blog
  /  

MonoRepository (for CDK libs and constructs) DevOps guide

Published: 11 Dec 2022
Oleh Tsymbal
Oleh Tsymbal
Founder, Cloud Solutions Architect

A comprehensive step-by-step Guide on how to install, set up, and run Monorepository for CDK libs and constructs. The following Guide was prepared by Cloud Architect and RabbitPeepers Co-Founder.

By the end of the article, you will learn how to create cloud infrastructure in CDK AWS from scratch, organize workspaces, operate constructs, and utilize Shared Packages in your projects.

For your convenience we leave the direct link to our GitHub repository with libriaries included below, feel free to find it directly on Git:
https://github.com/rabbitpeepers/cdk-monorepo-libs

CDK (AWS)

AWS CDK is a library that helps to create cloud infrastructure in AWS Cloud. It performs almost the same things as CloudFormation templates, but uses programming languages instead of YAML or JSON files. Mostly you need it when your applications consist of many microservices and workflows, and are not suitable if your application is just one single web page.
Read more at
https://aws.amazon.com/cdk/

Monorepo

Monorepository is a common pattern for organizing our code. It is a way to simplify the structure of all projects and subprojects. Monorepositories can be created via npm, yarn, pnpm, etc. Projects inside a monorepository are called workspaces.

Root Space initialisation

First of all, you have to have git and npm (nodejs) installed on your PC, as well as a VCS(Github, BitBucket) account.

Important: always initialize your repo with the main branch, not the dev.

npm init git init -b main

fix the engine versions in package.json

"engines": { "npm": ">=8.1.0", "node": ">=14.17.2" },

install some packages (if it wasn’t installed)

npm install -D @types/node@latest aws-cdk@latest typescript@latest

if you want to use jsii you have to install

"typescript": "~3.9.7"

so in package.json, you will have something like the following (don’t copy this content entirely with versions), also don’t forget to add cdk alias:

1"devDependencies": { 2 "@types/node": "^18.11.9", 3 "aws-cdk": "^2.51.1", 4 "typescript": "~3.9.7" 5}, 6 7"scripts": { 8 "cdk": "cdk" 9}, 10

add into .npmrc

engine-strict=true

Shared Configs

typescript

in order to share typescript config we have to create 2 typescript files (in the root directory)

touch tsconfig.shared.json
1{ 2 "compilerOptions": { 3 "target": "ES2020", 4 "module": "commonjs", 5 "lib": ["es2020"], 6 "declaration": true, 7 "strict": true, 8 "noImplicitAny": true, 9 "strictNullChecks": true, 10 "noImplicitThis": true, 11 "alwaysStrict": true, 12 "noUnusedLocals": false, 13 "noUnusedParameters": false, 14 "noImplicitReturns": true, 15 "noFallthroughCasesInSwitch": false, 16 "inlineSourceMap": true, 17 "inlineSources": true, 18 "experimentalDecorators": true, 19 "strictPropertyInitialization": false, 20 "typeRoots": ["./node_modules/@types"] 21 }, 22 "exclude": ["node_modules", "cdk.out"] 23} 24

and tsconfig.json

touch tsconfig.json
1{ 2 "extends": "./tsconfig.shared.json", 3 "compilerOptions": { 4 "baseUrl": "./" 5 }, 6 "include": ["./constructs/*/src/*/**.ts"] 7} 8

Important: You have to avoid using wildcards * whenever you can, but for demonstration purposes it is very fine.

Root Packages

Husky

To have pre-commit hooks we have to install husky into the global workspace

npm install -D husky@latest

run it via

npx husky install

put the pre-commit file with content to the .husky folder

#!/bin/sh . "$(dirname "$0")/_/husky.sh" npm run lint:constructs

and make it executable

chmod 700 .husky/pre-commit

add a script to the package.json

1 "scripts": { 2 "cdk": "cdk", 3 "lint:constructs": "npm -w constructs/* run lint", 4 "test": "echo \"Error: no test specified\" && exit 1" 5 }, 6

it will allow you to check all constructs before the commit.

Shared Packages

eslint

There are two ways how we can share eslint config:

  • one is installing it via package managers(npm, yarn)
  • the second approach is to create a shared config inside eslint-config-custom folder

Here we take look at the second approach

Create a dir(workspace) inside shared folder

npm -w shared/eslint-config-custom --scope=@shared init -y --engine-strict npm -w shared/eslint-config-custom --scope=@shared install

Install the required packages in this workspace:

npm -w @shared/eslint-config-custom install -D @typescript-eslint/eslint-plugin@latest \ eslint-config-prettier@latest eslint-plugin-jest@latest \ eslint@latest prettier@latest @types/prettier@latest

If you haven’t performed an install, you have to run the latest command twice, so you should check your package.json file and ensure that dependencies have been installed.

create a file index.js and fill it via your configurations e.g.

1// eslint-disable-next-line no-undef 2module.exports = { 3 root: true, 4 parser: "@typescript-eslint/parser", 5 plugins: ["@typescript-eslint", "jest"], 6 extends: [ 7 "eslint:recommended", 8 "plugin:@typescript-eslint/recommended", 9 "prettier", 10 "plugin:jest/recommended", 11 ], 12}; 13

install this configuration into your root workspace

add to package.json

"devDependencies": { "@shared/eslint-config-custom": "*", },

and fill out the config file

touch .eslintrc.js

1// eslint-disable-next-line no-undef 2module.exports = { 3 root: true, 4 extends: ['@shared/eslint-config-custom'] 5}; 6

don’t forget to install eslint in devDependencies

npm install -D eslint

prettier

Notice: we will use prettier@2.6.2 here because it uses typescript 3.9 under the hood, as well as Jsii libraries. Some constructs still use 3.9 in their dependencies, but if you want to use simple typescript constructs – install the latest versions.

There are also two ways how we can share prettier config:

  • one is installing it via package managers(npm, yarn), you have to build it
  • the second approach is to create a shared config inside a shared folder

Here we will implement the second approach.
Create a dir(workspace) inside shared folder.

npm -w shared/prettier-config-custom --scope=@shared init -y --engine-strict npm -w shared/prettier-config-custom --scope=@shared install npm -w shared/prettier-config-custom --scope=@shared install -D prettier@2.6.2

generate a file shared/prettier-config-custom/index.js and fill it out via your configurations e.g.

1// eslint-disable-next-line no-undef 2module.exports = { 3 singleQuote: true, 4 bracketSpacing: true, 5 tabWidth: 2, 6 trailingComma: 'none' 7}; 8

add this file to your root directory in the file (./package.json)

"devDependencies": { "@shared/prettier-config-custom": "*", },

and create the config .prettierrc.js that will use our shared configuration.

touch .prettierrc.js
module.exports = { ...require("@shared/prettier-config-custom"), };

check it in your files (make double quotes in your .js code, and if everything works well you will see that Prettier will change it to single quotes)

Lib(App) Workspace Initialisation

Before we kickoff, we have to decide what type of project we are going to start. It can be a simple library that will contain bare L1 or L2 constructs, or it can be an application that will use our constructs and eventually be generated into CloudFormation templates. Don’t combine these two types of projects in the same repository if at all possible, and if you have a mono repository for constructs(libraries) - don’t mix it with applications.

--scope parameter is very important if we want to publish our constructs to external registries, take a look at the official documentation
https://docs.npmjs.com/cli/v8/using-npm/scope?v=true

Here we create simple library

npm -w constructs/ms-teams-webhook --scope=@constructs init -y --engine-strict

unfortunately due to the bug https://github.com/aws/aws-cdk/issues/9609 we can’t easily init CDK project in non-empty dirs, so we will remove the content manually

# (zsh) workaround to rm everyhing without a confirmation setopt rm_starsilent rm -rf constructs/ms-teams-webhook/* rm -rf constructs/ms-teams-webhook/.*

go to this folder and init your CDK application

cd constructs/ms-teams-webhook npm exec cdk -- init lib --language=typescript # if you want to create an app use # npm exec cdk -- init app --language=typescript

replace tsconfig.json in the constructs/ms-teams-webhook directory in order to have common tsconfig.json file for all our libraries\applications

1{ 2 "extends": "../../tsconfig.shared.json", 3 "compilerOptions": { 4 "baseUrl": ".", 5 "composite": true, 6 "outDir": "dist", 7 "rootDir": "src" 8 }, 9 "include": ["src"] 10} 11

move your nested folders (in general lib and test) into the src directory

mkdir -p src && mv lib src/ && mv test src/

change required paths in package.json (it will be useful for jsii)

"directories": { "lib": "src/lib", "test": "src/test" },

and delete the node_modules folder and the package.lock file

rm -rf node_modules && rm -rf package.lock

change your paths in the packages.json

"main": "dist/lib/index.js", "types": "dist/lib/index.d.ts",

finally, change the name of your construct

"name": "@constructs/ms-teams-webhook",

If you are building the application (not library) add some scripts in our package.json

"scripts": { "synth": "cdk synth --output=cdk.out", },

add our result folders into .gitignore (inside workspace)

dist # if you create app # cdk.out

return back to the root project directory and run install

cd ../../ npm install

it will create a shared node_modules folder

don’t forget to install some modules

npm install -w constructs/ms-teams-webhook -D @types/prettier@2.6.0

ESlint for app\lib

add in (ms-teams-webhook workspace) package.json file
the following content

"devDependencies": { "@shared/eslint-config-custom": "*", }

after it, you can use it in your app|lib workspaces through .eslintrc.js file

1// eslint-disable-next-line no-undef 2module.exports = { 3 root: true, 4 extends: ['@shared/eslint-config-custom'] 5}; 6

don’t forget to add to your .gitignore file

# Exclude eslint shared config !.eslintrc.js

and remember to add the script into your project package.json

"scripts": { "lint": "eslint . --ext .ts" }

To make sure that everything is working (you will see some errors)

npm -w constructs/ms-teams-webhook run lint

Jest for app

change the file jest.config.js to

1// eslint-disable-next-line no-undef 2module.exports = { 3 testEnvironment: 'node', 4 roots: ['./src/test'], 5 //testMatch: ['./src/**/*.test.ts'], 6 transform: { 7 '^.+\\.tsx?$': 'ts-jest' 8 } 9 //moduleFileExtensions: ['js'] 10}; 11

and run tests via (it should pass tests)

npm -w constructs/ms-teams-webhook run test

Here we will describe some bugs that can be solved in the future

Unfortunately, commands like

npm install -w @constructs/slack-bitbucket-status-webhook -D @shared/eslint

will not work.

In case https://github.com/npm/cli/issues/3847 bug is fixed we can use something like this, but NOT Sooner

If you add shared packages manually (via package.json as it was described in this document) - it should work.

still doesn’t support typescript 4.x

https://github.com/aws/jsii/issues/3609

Sometimes during installation, you can face the problem

https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/60314

In this case, check your package.lock for having "@types/prettier": "2.6.0" and not "@types/prettier": "2.7.0", for applications - you can use the latest versions (due to typescript 4.x)

TO-DO

  • move to pnpm, rush, or turporepo.

https://dev.to/mbarzeev/sharing-configurations-within-a-monorepo-42bn

Clap if you like it!

Get in touch to discuss your business idea

Let's Start

Interested in our services?

Checked

What's next?

1
We contact you
We’ll contact you within 24 hours with more information on our next steps. In the meantime, you can check out our case studies and our blog
2
Collect requirements
Our next step will be to collect all the requirements for your project, clarify your business objectives, and expectations towards our cooperation
3
Proposal
After that, we’ll develop a proposal for you.

Services

  • Development
  • Advisory
  • Infrastructure
RabbitPeepers LLC
contact@rabbitpeepers.com
Kyiv, Ukraine
Delaware, US
PrivacyTerms © 2019-2023