5 Things that should be Automated in a TypeScript/Javascript Monorepo
Managing a TypeScript monorepo can be a complex task with various moving parts. Automation ensures that everything runs smoothly, from dependency management to deployment. Automate these five things in a TypeScript or JavaScript monorepo to enhance productivity and maintainability.
1. Renovate for package.json Updates
Why Automate This?
I've included this first because managing dependencies is a huge pain point in monorepos. Leaving the updating of each package.json
file to a developer or, even worse, developers often leads to failures, especially when multiple packages are within the monorepo.
How to Automate
Use renovate to scan the monorepo repository automatically and generate Pull Requests to update dependencies. I like to confine my PRs to the weekend to keep the noise down, but this is all configurable through the extensive config options.
Below is a view of some PRs created by the excellent renovate.
.
Each PR includes scores for confidence and adoptions for the dependency as well as the release notes from the PR for the dependency that is updated:
.
With renovate, it is possible to specify scheduling, grouping, and even auto-merge rules.
Below is an example renovate.json
for a monorepo that I control:
{
"extends": ["config:base", "group:monorepos"],
"baseBranches": ["main"],
"semanticCommitScope": "deps",
"rangeStrategy": "bump",
"rebaseLabel": "rebase",
"rebaseWhen": "auto",
"requiredStatusChecks": [],
"timezone": "Europe/Dublin",
"schedule": ["before 8am every weekend"],
"lockFileMaintenance": {
"enabled": false
},
"packageRules": [
{
"packageNames": ["node", "pnpm"],
"enabled": false
},
{
"depTypeList": ["peerDependencies"],
"enabled": false
},
{
"updateTypes": ["major", "minor", "patch", "pin", "digest"],
"automerge": true
},
{
"depTypeList": ["devDependencies"],
"automerge": true
},
{
"packagePatterns": ["eslint"],
"groupName": "eslint"
}
]
}
Benefits
- Keeps dependencies up-to-date, reducing security vulnerabilities.
- Frees developers from the manual task of updating each package.json.
2. Adobe changesets for Versioning and Changelog
Why Automate This?
In a monorepo, tracking changes across multiple packages can get chaotic. Leaving this to the diligence of the developer will always lead to problems. A versioning system helps maintain order. A naive approach is to use git commit messages to update the CHANGELOG.md
. Dated git commit messages will lead to a suboptimal CHANGELOG
.
How to Automate
Adobe changesets offers a way to automate versioning and changelog updates. It provides commands to create changesets that describe the changes in your code.
An interactive CLI will prompt the user for inputs to create the changeset and provide a proper message for the CHANGELOG
:
The following two links provide complete information on how to get started with changesets:
Benefits
- Streamlined versioning across multiple packages.
- Automatic generation of a detailed changelog.
3. Automate performance metrics
Why Automate This?
Staying on top of performance metrics is critical. Do not wait until your users start complaining. You should know exactly where you stand on every commit.
How to Automate
I use WebpageTest, which offers detailed performance metrics and allows tests from multiple geographical locations, simulating various browsers and network conditions. WebpageTest enables developers to identify bottlenecks, optimize for different user scenarios, and improve overall user experience.
I have the following step in a GitHub action that supplies an array of URLs for WebpageTest to test.
- name: Trigger webpage test
if: github.event_name == 'pull_request'
uses: peter-evans/repository-dispatch@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
repository: dagda1/cuttingedge
event-type: trigger-webpage-test-for-frontendsupport
WebpageTest will automatically send an email and add a comment on the pull request for every commit to a pull request with detailed metrics:
Benefits
- Know the moment performance starts going south
- Communicate performance metrics to all stakeholders
4. TurboRepo for Build and Test Acceleration
Why Automate This?
Building and testing in a monorepo can be slow, especially as the codebase grows.
How to Automate
Turborepo accelerates this process by only building and testing the parts of your monorepo affected by individual commits.
Benefits
- Faster build and test cycles.
- Resource optimization, as only relevant tasks are executed.
5. Automated Deployment
Why Automate This?
It seems ridiculous to think manual deployments still happen. However, I have included automated deployments just in case anyone is still practising the dark art of throwing the dice and hoping for a six each time. Manual deployments are error-prone and can be a bottleneck in the development process.
How to Automate
Use CI/CD pipelines with tools like GitHub Actions or GitLab CI/CD to automate the deployment process.
The website that you are currently reading uses the GitHub action below to deploy a remix-run application that deploys an AWS architect application to both production
and staging
variants depending on the context that the action is run in.
name: Build and deploy
on: [push, pull_request]
env:
CI: true
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
jobs:
deploy:
environment: CI
name: build and deploy to AWS
runs-on: ubuntu-latest
steps:
- uses: pnpm/action-setup@v2.4.0
with:
version: 8.7.4
- name: Checkout Repo
uses: actions/checkout@v3
- name: Setup Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: "pnpm"
- name: Cache pnpm modules
uses: actions/cache@v3
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-
- name: Install Dependencies
run: pnpm i --no-frozen-lockfile
- name: Build App
run: pnpm build
- name: run architect deploy
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'pull_request'
working-directory: apps/frontendsupport
run: |
FLAGS=""
if [[ "${{ startsWith(github.ref, 'refs/tags/v') }}" == true ]]; then
FLAGS="--production"
else
FLAGS="--staging"
fi
GIT_COMMIT="$(git rev-parse HEAD)" pnpm arc deploy $FLAGS -v --prune
env:
NODE_ENV: production
- name: Invalidate Cloudfront cache staging
if: github.ref == 'refs/heads/main'
uses: imehedi/actions-awscli-v2@latest
with:
args: cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_STAGING_ID }} --paths "/*" --no-cli-pager
- name: Invalidate Cloudfront cache Production
if: startsWith(github.ref, 'refs/tags/v')
uses: imehedi/actions-awscli-v2@latest
with:
args: cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_PRODUCTION_ID }} --paths "/*" --no-cli-pager
Benefits
- Consistent and reliable deployments.
- Faster release cycles.