If you are planning to develop multiple applications working together, using the same technology, it is advantageous to place all of them in the same Git repository. This way you can use relative paths to refer to shared functions to access the same database, React components, and images to make the pages look consistent. As all are in the same repository, the CI/CD pipeline will also find those during application building and testing.
If you use Node.js packages, use PNPM for package management to rationalize the node modules and store them in the top level node_modules directory and use symbolic links to point to those from other locations. See Install pnpm.
To set up the mono repo for multiple Node.js applications
Create the directory structure
Create the root directory of the repository and the apps and packages subdirectories.
This file tells PNPM where the applications and shared packages will be to be able to reuse node-packages. ** tells PNPM to search for package.json files recursively.
packages:
- "apps/**"
- "packages/**"
Add the applications
If you already have existing applications, copy their directory structures into the apps directory and delete the node_modules directories. PNPM will recreate those and create symbolic links to point to the top level node_modules directory.
As we use PNPM to rationalize the node modules for all applications, do not install the node modules in the individual application directories. Once the application structure been created open a terminal in the root of the mono repo and execute
pnpm install
Based on the packages list in the pnpm-workspace.yaml file, PNPM will install all necessary node modules for all applications and packages in the entire workspace, which is the current mono repo.
If an unidentified process is generating a web site
Try to load it in an incognito / Private window Open http://localhost:PORT/... in a brand new private window.
Works there too → real server is running.
Fails there → old tab is just showing cached content.
curl test
In a Windows or in WSL:
curl -v http://localhost:8081/
Connection refused / timeout → backend is dead.
HTTP 200 with HTML → something is definitely serving.
Who owns the port
In a Windows PowerShell window with Admin rights:
netstat -abno | findstr :8081
In a Windows PowerShell window with Admin rights:
tasklist /FI "PID eq <PID from above>"
In WSL (if wslrelay.exe is involved):
sudo ss -lntp | grep 8081
sudo lsof -i :8081
To kill the processes in WSL, use the PIDs returned by the command above
sudo kill PID1 PID2
WARNING: To permanently disable the docker and docker.socket in WSL you may execute the following commands, but maybe those processes are necessary for the normal operation of the WSL system.
# 1. Disable Docker and its socket so they don't start automatically
sudo systemctl disable docker.service docker.socket
# 2. Stop the socket right now so nothing can auto-start dockerd in this session
sudo systemctl stop docker.socket
# 3. Sanity check
systemctl status docker
systemctl status docker.socket
If you want to create the Git repository on a higher level, answer NO to the Initialize a new git repository? question.
If you want to use PNPM as node package manager, answer NO to the Install dependencies with npm? question and issue the pnpm install -r command in the root folder of the repository to only have one real copy of the node modules. PNPM creates symbolic links from subdirectories to refer to the one file instance to save space on the hard drive.
Run the application
To run the web application, in the application root directory execute
WSL enables Windows computers to concurrently run Windows and Linux at the same time. Modern software development and deployment usually depends on Linux tools and commands. To be able to run a Linux container on a Windows workstation, we need the availability of the Linux Kernel.
To install Windows Subsystem for Linux (WSL)
To use the default Ubuntu distribution
Open a PowerShell terminal as Administrator and execute the command wsl --install
To install another distribution
Open a PowerShell terminal as Administrator and execute the command to get the list of available distributions wsl.exe --list --online
Install the desired distribution wsl --install -d MY_DISTRIBUTION_NAME
Create the Linux user
The installer will create a Linux user. By default it will use your Windows user name. Enter a password and retype it.
Create a default Unix user account: MY_USER_NAME
New password:
Retype new password:
Reboot the computer for the changes to take effect.
Configuration
Terminal
Install the Windows Terminal from the Microsoft App Store
Code editor
Configure Visual Studio Code to be able to access and debug in the Linux file system.
IMPORTANT: For the path changes to take effect close and re-open the terminal window, or close and re-open Visual Studio Code if you use the integrated terminal.
Share the credentials between the Windows and WSL Git instances
In Windows execute
git config --global credential.helper wincred
Create GitHub personal access tokens
In the GitHub Web interface create new Personal Access Tokens, one for Windows and one for WSL with the following scopes
Azure pipelines support the CI/CD (Continuous Integration, Continuous Deployment), the fully automated Continuous Delivery process.
Agents, either hosted by Microsoft, or self-hosted on the client’s infrastructure, execute the yaml scripts.
Steps
The smallest unit of work in the Azure Pipeline. Each step is one action in a job. A Step can be a Script or a Task.
Script
A Script is a command written in a scripting language to be executed as a step of a job.
Running a Bash Script
steps:
- script: echo "Hello, World!"
displayName: 'Script to say Hello'
Task
A Task is a packaged script that can be used as a step within a job:
Built-in tasks from Azure Pipeline
Custom scripts written in PowerShell, Bash, Python or other languages
Third-party tasks from the Azure DevOps Marketplace
Running a Task, to execute an Azure CLI command with attributes
steps:
- task: AzureCLI@2
displayName: 'Run Azure CLI Command'
inputs:
azureSubscription: 'MyAzureSubscription' # Service connection to Azure
scriptType: 'bash' # Specify the script type (bash or ps)
scriptLocation: 'inlineScript' # Define the script location
inlineScript: | # Start the inline script
echo "Listing all resources in the resource group..."
az resource list --resource-group MyResourceGroup
Jobs
The job is a single phase of the pipeline executing multiple steps. Jobs can run in parallel or in sequence if those depend on each other. It has its own context and workspace, so variables and files created in one job are kept separate from each other. This allows the easier troubleshooting of the process. Typical steps are:
Building code
Running tests
Stages
Stages hold related jobs together. It can represent
Environments, like
Development,
QA,
Production,
or Related jobs, like
Build,
Test,
Deploy.
Triggers
Triggers tells the pipeline to run. It can be based on
Events, like
A commit to a repository or
A pull request
or Schedules
To run at specific times, like end of the day.
Approval
Gatekeeper before the stage can proceed. The pipeline execution pauses until the approval arrives. It can be
Manual (like human approval is required before deployment to Production)
Automatic (like only during business hours, or when automated tests are passed)