As Microservices architecture becomes popular recently, Monorepo is also in trend right now as a software development concept. If you don't know what a Monorepo is, I recommend you to read Why you should use Monorepo and why you should not first.
One of the biggest challenges of a Monorepo is how to optimize the deployment pipeline and versioning for your apps and packages. In this demonstration, I'll show you how.
- Real use cases
- Blog posts
- Tools I used
- Why versioning is important
- What is ArgoCD
- Demo Architecture
- Deployment and Versioning Demo
- Sequence Diagram
- How can I setup the CI/CD pipeline for my monorepo?
- FAQ
- Reference
-
มาทำ Versioning + GitOps + K8s บน Monorepo กันเถอะ! Part I - Introduction: How important is Monorepo, versioning and GitOps? And what are the challenges?
-
มาทำ Versioning + GitOps + K8s บน Monorepo กันเถอะ! Part II - Versioning Tutorial: What is versioning and how to do it in Monorepo? What tools can we use?
-
มาทำ Versioning + GitOps + K8s บน Monorepo กันเถอะ! Part III - A whole development process: How to work with Gitflow and where to apply versioning and GitOps in our workflows?
-
มาทำ Versioning + GitOps + K8s บน Monorepo กันเถอะ! Part IV - The Implementation: How to implement the whole development process in Part III? What each step of the workflow is?
-
🚧 มาทำ Versioning + GitOps + K8s บน Monorepo กันเถอะ! Part V - How to pre-release: How to pre-release your packages and apps? What is the workflow? It's specially difficult when you have a monorepo.
- Monorepo management tool - Turborepo
- Versioning tool - Changesets and Changesets Action
- CI/CD tool - GitHub Actions
- GitOps tool - ArgoCD
Versioning is important because it helps you track changes in your apps and packages from the CHANGELOG. It also enables you to understand what's new in the latest version of your apps and packages, and it indicates the type of changes made (such as feature, bug fix, breaking change, etc.).
To learn more about versioning, visit: https://en.wikipedia.org/wiki/Software_versioning.
ArgoCD is a GitOps tool that helps you manage your apps and packages in K8s using a declarative approach. You can learn more about ArgoCD at https://argoproj.github.io/argo-cd/.
ArgoCD follows the pull model, which means that it pulls the image with a specific version defined in the K8s manifest file.
In contrast, traditional CI/CD pipelines follow the push model. This means that you must apply the K8s manifest file to the cluster after the CI/CD pipeline is finished. Additionally, you may need to set up credentials for the CI/CD pipeline to access the cluster, which can lead to security issues.
To learn more about GitOps and the differences between the push model and the pull model, here https://faun.pub/gitops-comparison-pull-and-push-88fcbaadfe45.
Here is the application architecture for this repository. It consists of three applications and one shared package. The shared package contains code that is common across the applications except api, while each of the three applications has its own unique functionality. The architecture is designed to be simple and easy to understand, making it a great starting point for developers who are new to the repository.
---
title: Apps
---
classDiagram
Web <|-- UI
Docs <|-- UI
class api
Versionning and deployment workflow is based on GitFlow, which is a branching model for Git.
Assume that you have 3 application's environments.
- Production - This environment is for end-users and is located in the
main
branch. - Staging (Beta) - This environment is for testing new features and bug fixes before releasing them to production. It's located in the
beta
branch. - Development - This environment allows developers to test their code by deploying it for testing purposes without reviewing before opening PRs to
beta
. It's located in thedev
branch.
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'rotateCommitLabel': false}}}%%
gitGraph
commit id: "release v1.0.0" tag: "v1.0.0"
branch beta
checkout beta
commit id: "branch from v1.0.0"
branch feature-1
branch feature-2
branch dev
checkout feature-1
commit id: "develop feature 1"
checkout dev
merge feature-1 id: "merge feature-1 into dev"
checkout feature-2
commit id: "develop feature 2"
checkout dev
merge feature-2 id: "merge feature-2 into dev"
checkout beta
merge feature-1 id: "merge feature-1 into beta"
merge feature-2 id: "merge feature-2 into beta"
branch changeset/beta
checkout changeset/beta
commit id: "versioning beta"
checkout beta
merge changeset/beta id: "merge changeset/beta into beta" tag: "v1.1.0-beta.0"
checkout main
merge beta id: "merge beta into main"
branch changeset/main
checkout changeset/main
commit id: "versioning main"
checkout main
merge changeset/main id: "merge changeset/main into main" tag: "v1.1.0"
- When you and your team want to develop new features or bug fixes, you may need to create a new branch from the
beta
branch. This new branch is called afeature
branch. - After you finish your code changes, you can merge the
feature
branch into thedev
branch without opening a PR. GitHub Actions will automatically deploy your changes to thedev
environment, allowing you to test your code without versioning.
-
If the
dev
environment is working correctly, you can open a PR from thefeature
branch to thebeta
branch. This will let reviewers know what's new in this PR. -
Don't forget to add a changeset file to describe the changes in your apps and packages. You can read more about adding changeset here.
-
After the PR is approved and merged (using any merge strategy e.g. merge commit, squash commit), GitHub Actions will automatically create a new PR called Version Packages (Beta) that targets the
beta
branch. This PR will summarize the changes made to your apps and packages in this release, as illustrated below: -
After the PR is merged, GitHub Actions will automatically publish your affected apps to the Container Registry, which in this case is GitHub Container Registry. Additionally, GitHub Actions will automatically create a GitOps PR called Update GitOps to bump the version of the image in K8s manifests, as illustrated below:
This step take advantage of GitOps to manage K8s manifests.
This step can take a significant amount of time to complete, as it involves building Docker images for your affected apps and pushing them to the Container Registry.
-
If you want to officially release your apps and packages to the production environment, you can open a PR from the
beta
branch to themain
branch. At this step, you should ask the contributors if their changes work in the staging environment. -
After the PR is approved and merged (with any merge strategy), like staging deployment, GitHub Actions will open a PR called Version Packages to
main
branch. This PR will summarize the changes of your apps and packages in this release, like this. -
After the PR is merged, your affected apps will be published to the Container Registry with the bumped version, similar to the staging deployment. Additionally, an Update GitOps PR will also be opened to bump the version of the image in the K8s manifests.
-
Merge the Update GitOps PR to the
main
branch. This will automatically deploy your apps to the production environment.
Note: Every time the K8s manifests are updated, ArgoCD will automatically deploy your apps to the affected environment.
The flow of development extended from GitFlow and the versioning workflow comes from Changesets Release Action. The following diagram shows the flow of development and versioning, as I explained above.
sequenceDiagram
Developer->>GitHub: Checkout from 'beta' branch <br/> called 'feature-1'
Developer->>Developer: Develop new features
Developer->>GitHub: Push code to 'dev' branch
GitHub->>GitHub Action: Execute <br/>'release-without-versioning' <br/> workflow
GitHub Action ->> Container Registry: Publish affected apps <br/> with 'git-sha' hash
GitHub Action -->> GitHub: Commit and push updated <br/> k8s manifest files
GitHub ->> ArgoCD: Argocd is watched the k8s manifest. <br/> It will automatically deploy affected apps to dev environment
After this step, you, who implemented the new features, should test your apps in the dev environment. If everything works properly, you can move to the next step.
---
title: Merge the feature PR to beta
---
sequenceDiagram
Developer->>GitHub: Open PR from 'feature-1' to 'beta'
par Enter Pre-release mode
GitHub ->> GitHub Action: Execute 'pre-release' workflow
GitHub Action -->> GitHub: Enter changesets' pre mode <br/> if the mode is not pre
and Changesets Bot
GitHub ->> Changesets Bot: Notify PR is opened to 'beta' branch
Changesets Bot -->> GitHub: Add a comment to the PR <br/> to remind contributors to add a changeset file
Developer->>GitHub: Add a changeset file
Changesets Bot -->> GitHub: Update the summary of changeset file
end
Developer->>GitHub: Merge the PR
GitHub ->> GitHub Action: Execute 'release' workflow
GitHub Action -->> GitHub: Open a PR called 'Version Packages (Beta)' <br/> to 'beta' branch
The Version Packages (Beta) is a PR created by Changesets Release Action in order to summarize the changes of your apps and packages in this pre-release.
---
title: Release to the staging environment
---
sequenceDiagram
Developer->>GitHub: Merge the release PR
par GitHub Releases
GitHub ->> GitHub Action: Execute 'release' workflow
GitHub Action -->> GitHub: Update 'GitHub Releases'
GitHub Action -->> Container Registry: Publish affected apps <br/> with the bumped 'semver' version
GitHub Action -->> GitHub: Open a PR called 'Update GitOps'
and Sync 'beta' branch to 'main' branch
GitHub ->> GitHub Action: Execute 'sync-beta' workflow
GitHub Action -->> GitHub: Rebase 'beta' branch to 'main' branch
end
Developer ->> GitHub: Review and merge the 'Update GitOps' PR
Note over Developer, GitHub: 'Update GitOps' PR is created by <br/> 'update-gitops' workflow in order to confirm <br/> the changes of k8s manifest files
Note over Developer, GitHub: This PR may be in the another <br/> repository.
GitHub ->> ArgoCD: Argocd is watched the k8s manifest. <br/> It will automatically deploy affected apps to staging environment
After this step, you might see the new feature in the staging environment.
Assuming that the new features on the staging environment work properly, you may want to officially release your apps and packages to the production environment.
This step is simpler than the pre-release step.
---
title: Merge the beta to main
---
sequenceDiagram
Developer->>GitHub: Open PR from 'beta' to 'main'
par Exit Pre-release mode
GitHub ->> GitHub Action: Execute 'pre-release' workflow
GitHub Action -->> GitHub: Exit changesets' pre mode <br/> if the mode is not exit
end
Developer ->> Developer: Waiting for the relevant developers review
Developer->>GitHub: Merge the PR
GitHub ->> GitHub Action: Execute 'release' workflow
GitHub Action -->> GitHub: Open a PR called 'Version Packages' <br/> to 'beta' branch
---
title: Release to the production environment
---
sequenceDiagram
Developer->>GitHub: Merge the release PR
par GitHub Releases
GitHub ->> GitHub Action: Execute 'release' workflow
GitHub Action -->> GitHub: Update 'GitHub Releases'
GitHub Action -->> Container Registry: Publish affected apps <br/> with the bumped 'semver' version
GitHub Action -->> GitHub: Open a PR called 'Update GitOps'
and Sync 'beta' branch to 'main' branch
GitHub ->> GitHub Action: Execute 'sync-beta' workflow
GitHub Action -->> GitHub: Rebase 'beta' branch to 'main' branch
end
Developer ->> GitHub: Review and merge the 'Update GitOps' PR
Note over Developer, GitHub: 'Update GitOps' PR is created by <br/> 'update-gitops' workflow in order to confirm <br/> the changes of k8s manifest files
Note over Developer, GitHub: This PR may be in the another <br/> repository.
GitHub ->> ArgoCD: Argocd is watched the k8s manifest. <br/> It will automatically deploy affected apps to production environment
As a developer, you don't have to perform any manual deployment tasks to release the new feature to a specific environment. All you have to do is merge the relevant PRs.
There are things you need to do if you already have your monorepo workspace set up, but for the most part, all you need to do is:
- Setup Changeset configuration
- Setup ArgoCD for your k8s cluster
- Copy/paste all workflows under .github/workflow from this repository. (Make sure you understand what it's doing)
- Setup the secrets,
GH_TOKEN
(GitHub PAT withrepo
permission to let Action open the 'Update GitOps' PR)
-
Install Changesets CLI in the root of the workspace:
pnpm add -D -W @changesets/cli
-
Initialize Changesets:
pnpm changeset init
At this point, two files will be created:
.changeset/config.json .changeset/README.md
-
Configure the
config.json
file:{ "$schema": "https://unpkg.com/@changesets/[email protected]/schema.json", - "changelog": "@changesets/cli/changelog", + "changelog": ["@changesets/changelog-github", { "repo": "ORGANIZATION_NAME/REPO_NAME" }], "commit": false, "fixed": [], "linked": [], "access": "restricted", "baseBranch": "main", "updateInternalDependencies": "patch", "ignore": [] }
changelog
- This specifies the changelog generator used to generate the changelog for each package. In this case, we use the GitHub changelog generator.
Be sure to replace
ORGANIZATION_NAME
andREPO_NAME
with your own GitHub organization and repository name. -
Add Changesets bot for your repository.
-
Set up the GitHub Action workflow for releasing apps and packages. You can copy my action YAML here.
-
For the apps that you want to build as Docker images, you need to add a
Dockerfile
to each of them. You can copy my Docker file here- NextJS app - Dockerfile
- NestJS app - Dockerfile
Note the workflow will skip to build Docker images if there is no
Dockerfile
in the app. But it will still bump the version of the app. -
You're done! Now you're ready to use Changesets to release your apps and packages.
Please following the official documentation to setup ArgoCD for your k8s cluster.
Or, follow the casual version of setup ArgoCD here
Install ArgoCD in argocd
namespace
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
Change the argocd-server
service type to LoadBalancer
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
Get the default password of admin
account, please wait for a while until the argocd-initial-admin-secret
is created.
echo $(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d)
Setup Application for ArgoCD
kubectl apply -f ./k8s/argo
It'll create apps for every environment that defined in apps.
Finally, run the follwing command to port-forward the ArgoCD dashboard.
kubectl port-forward svc/argocd-server -n argocd 8080:443
The dashboard will be available at https://localhost:8080
-
What if I forgot to create a changeset file for my PR?
Answer:
You can create a changeset file for your change manually by running
pnpm changeset
Then, you can commit the changeset file and push it to the remote branch.
-
What if I misspell the changeset file content?
Answer:
You can fix the changeset file's content any time you want before you merge the release PR. Make sure to review the changeset file every time.
-
What if I want to 2-step pre-release e.g.
alpha
for the first pre-release andbeta
for the second pre-release?Answer:
It's not possible for now. Since the Changeset does not support it right now.
Many thanks to the authors and contributors of the following articles and projects, which provided valuable insights and inspiration for this article.