Skip to content

Commit 4162fee

Browse files
itaispiegelclaude
andauthored
docs: add monorepo plugin documentation (#835)
Companion to apache/devlake#9037 (Plugin for per-sub-project DORA metrics in a monorepo). Adds a Plugins/monorepo page covering how deployments are attributed by CI job name and pull requests by label, the per-project configuration on the project settings page and its equivalent metric plugin option, the unattributed/All reserved names, what the plugin writes, and the built-in Monorepo Sub-Projects dashboard. It also states that only Deployment Frequency and Lead Time for Changes are computed per sub-project, since incidents are not attributed. Documents the new sub_project columns on pull_requests, pull_request_commits and project_pr_metrics plus the new cicd_deployment_subprojects table in the domain layer schema, and links to the plugin from the DORA page. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 593f0ed commit 4162fee

3 files changed

Lines changed: 175 additions & 0 deletions

File tree

‎docs/DORA.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,17 @@ If you are not familiar with Grafana, please refer to our [Grafana doc](Configur
206206

207207
<br/><br/>
208208

209+
## DORA for a monorepo
210+
211+
DevLake measures DORA at the project level, and a project's scope is a repository. If a
212+
single repository hosts several logically separate projects — each with its own deploy job
213+
and its own PR label — all of them collapse into one set of DORA numbers.
214+
215+
The [monorepo plugin](Plugins/monorepo.md) splits such a repository into sub-projects and
216+
computes per-sub-project Deployment Frequency and Lead Time for Changes.
217+
218+
<br/>
219+
209220
## Try it Out
210221

211222
To create the DORA dashboard with your own toolchain, please look at the [configuration tutorial](Configuration/Tutorial.md) and [project organization documentation](Configuration/HowToOrganizeDevlakeProjects.md) for more details.

‎docs/DataModels/DevLakeDomainLayerSchema.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ Pull requests are the abstraction of GitHub pull requests, GitLab merge requests
465465
| `head_ref` | varchar | 255 | The branch name in the head repo that contains the changes that will be added to the base | |
466466
| `base_commit_sha` | char | 40 | The base commit of this PR. | |
467467
| `head_commit_sha` | char | 40 | The head commit of this PR. | |
468+
| `sub_project` | varchar | 100 | The monorepo sub-project this PR belongs to, matched by label. Written by the [monorepo plugin](../Plugins/monorepo.md); NULL when the project has no monorepo configuration. | |
468469

469470
#### pull_request_labels
470471

@@ -489,6 +490,7 @@ The list is additive. This means if a rebase with commit squashing takes place a
489490
| `commit_author_name` | varchar | 255 | The name of the person who authored the commit | |
490491
| `commit_author_email` | varchar | 255 | The email address of the person who authored the commit. | |
491492
| `commit_authored_date` | varchar | 255 | The date and time when the commit was authored. | |
493+
| `sub_project` | varchar | 100 | Mirrors the owning pull request's `sub_project`. | |
492494

493495
#### pull_request_comments
494496

@@ -656,6 +658,20 @@ Normally, one deployment only deploy to one repo. But in some cases, one deploym
656658
| `repo_url` | varchar | 191 | The url of the repo | |
657659
| `prev_success_deployment_commit_id` | varchar | 255 | The last successful deployment_commit_id before this one in the same `cicd_scope`, `repo` and `environment`, which is used to calculate the new commits deployed by this deployment, thereby measuring [DORA - change lead time](../Metrics/LeadTimeForChanges.md). | |
658660

661+
#### cicd_deployment_subprojects
662+
663+
Maps a deployment to the monorepo sub-project(s) it deployed. Rows are written by the [monorepo plugin](../Plugins/monorepo.md), which matches the name of the deploying CI job against each sub-project's configured pattern.
664+
665+
The relationship is many-to-many: a pipeline that runs the deploy jobs of several sub-projects produces one row per sub-project, so dashboards can `GROUP BY sub_project` without double counting.
666+
667+
Projects without monorepo configuration have no rows in this table at all; dashboards treat a missing mapping as the single, implicit `All` group.
668+
669+
| **field** | **type** | **length** | **description** | **key** |
670+
|:---------------------|:---------|:-----------|:------------------------------------------------------------------------------------------|:--------|
671+
| `project_name` | varchar | 100 | The project this mapping belongs to | PK |
672+
| `cicd_deployment_id` | varchar | 255 | The deployment, matching [table.cicd_deployment_commits](#cicd_deployment_commits).`cicd_deployment_id` | PK |
673+
| `sub_project` | varchar | 100 | The sub-project this deployment deployed | PK |
674+
659675
### Domain 5 - Code Quality
660676

661677
The names of tables in the 'Code Quality' domain will start with a prefix cq\_
@@ -929,6 +945,7 @@ metrics, such as _'No. of Issue closed by contributor', 'No. of commits by contr
929945
| `deployment_id` | longtext | | The id of cicd_task which deploy the commits of this PR | |
930946
| `pr_deploy_time` | bigint | | The time it takes from when a PR is merged to when it is deployed | |
931947
| `pr_cycle_time` | bigint | | The total time from the first commit to when the PR is deployed | |
948+
| `sub_project` | varchar | 100 | The monorepo sub-project of this PR, copied from [table.pull_requests](#pull_requests) | |
932949

933950
#### project_issue_metrics
934951

‎docs/Plugins/monorepo.md‎

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
---
2+
title: "Monorepo"
3+
description: >
4+
Monorepo Plugin
5+
---
6+
7+
## Summary
8+
9+
DevLake's scope model is one scope per repository, so a repository that hosts several
10+
logically separate projects collapses into a single set of DORA numbers. The `monorepo`
11+
plugin splits such a repository into **sub-projects** and computes per-sub-project
12+
delivery metrics.
13+
14+
Like [DORA](../DORA.md) and [Linker](./linker.md), it is a metric plugin: it works on the
15+
domain layer after data collection, and runs after `dora`.
16+
17+
It attributes:
18+
19+
- **Deployments** to a sub-project by matching the name of the CI job that performed the
20+
deployment against a regular expression.
21+
- **Pull requests** to a sub-project by matching the PR's labels.
22+
23+
From those two attributions it produces per-sub-project
24+
[Deployment Frequency](../Metrics/DeploymentFrequency.md) and
25+
[Lead Time for Changes](../Metrics/LeadTimeForChanges.md).
26+
27+
[Change Failure Rate](../Metrics/CFR.md) and
28+
[Median Time to Restore Service](../Metrics/MTTR.md) are **not** computed per sub-project,
29+
because they require incident data and incidents are not attributed to sub-projects.
30+
31+
## Supported Versions
32+
33+
The monorepo plugin was added after the `v1.0.3-beta18` release and is available in all
34+
later releases.
35+
36+
## Configuration
37+
38+
The plugin is configured per project, on the project's settings page.
39+
40+
1. Open **Projects** → your project → **Settings**.
41+
2. Tick **Enable Monorepo Sub-Projects**.
42+
3. Add one row per sub-project and fill in the three fields below.
43+
4. Click **Save**, then re-run the project's blueprint.
44+
45+
| Field | Description | Example |
46+
|:--------------------------|:----------------------------------------------------------------------------------------------------------------------------------------|:---------------------|
47+
| **Sub-project name** | Identifies the sub-project in the output tables and dashboards. Must be unique within the project. | `serviceA` |
48+
| **Labels** | Comma-separated pull request labels that mark a PR as belonging to this sub-project. Matching is **exact and case-sensitive**. | `serviceA,svc-a` |
49+
| **Deploy job pattern** | A [Go regular expression](https://pkg.go.dev/regexp/syntax) matched against `cicd_tasks.name` to recognise this sub-project's deploy jobs. | `^deploy-serviceA$` |
50+
51+
`unattributed` and `All` are reserved and cannot be used as sub-project names.
52+
53+
The same options can be set through the API or the Config UI's
54+
[advanced mode](../Configuration/AdvancedMode.md) as the project's `monorepo` metric
55+
plugin option:
56+
57+
```json
58+
{
59+
"pluginName": "monorepo",
60+
"enable": true,
61+
"pluginOption": {
62+
"subProjects": [
63+
{
64+
"name": "serviceA",
65+
"prLabels": ["serviceA", "svc-a"],
66+
"deployJobPattern": "^deploy-serviceA$"
67+
},
68+
{
69+
"name": "serviceB",
70+
"prLabels": ["serviceB"],
71+
"deployJobPattern": "^deploy-serviceB$"
72+
}
73+
],
74+
"includeUnattributed": true
75+
}
76+
}
77+
```
78+
79+
`includeUnattributed` (default `true`) controls what happens to pull requests and
80+
deployments that belong to the project but match none of the configured sub-projects:
81+
with `true` they are recorded under the sentinel sub-project `unattributed`, so you can
82+
see how much of the repository is not yet classified; with `false` they are left
83+
unclassified.
84+
85+
## How attribution works
86+
87+
### Deployments
88+
89+
Every deployment of the project is joined to the CI/CD tasks of the pipeline that produced
90+
it, and each task of type `Deployment` has its name matched against each sub-project's
91+
`deployJobPattern`.
92+
93+
- A pipeline that runs the deploy jobs of several sub-projects produces **one row per
94+
sub-project**, so dashboards can `GROUP BY sub_project` without double counting.
95+
- Deployments imported directly from a provider's deployment API (rather than generated
96+
from a pipeline) carry no job name and cannot be attributed.
97+
98+
### Pull requests
99+
100+
All pull requests of the project are attributed — open, closed and merged — by label.
101+
A pull request belongs to exactly one sub-project: when it carries the labels of more than
102+
one, the **first matching sub-project in the configured order wins**, so order the list
103+
from most to least specific.
104+
105+
PR coding, pickup, review, deploy and cycle time are not recomputed by this plugin; they
106+
are taken from DORA's own `project_pr_metrics` rows and tagged with the sub-project.
107+
108+
### Mismatch warnings
109+
110+
After tagging, the plugin cross-checks each PR's label-derived sub-project against the
111+
sub-project of the deployment that DORA says actually shipped it. Disagreements are logged
112+
as warnings and never fail the pipeline — labels always win. They usually point at a
113+
`prLabels` or `deployJobPattern` entry that needs fixing, though a pipeline deploying
114+
several sub-projects at once will produce such warnings legitimately.
115+
116+
## Output
117+
118+
| Table | Description |
119+
|:------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------|
120+
| [`cicd_deployment_subprojects`](../DataModels/DevLakeDomainLayerSchema.md#cicd_deployment_subprojects) | Maps each deployment to the sub-project(s) it deployed. |
121+
| [`pull_requests.sub_project`](../DataModels/DevLakeDomainLayerSchema.md#pull_requests) | The sub-project a pull request belongs to. |
122+
| [`pull_request_commits.sub_project`](../DataModels/DevLakeDomainLayerSchema.md#pull_request_commits) | Mirrors the owning pull request's sub-project. |
123+
| [`project_pr_metrics.sub_project`](../DataModels/DevLakeDomainLayerSchema.md#project_pr_metrics) | Tags DORA's per-PR metrics with the sub-project. |
124+
125+
A `NULL` `sub_project` means the row was never classified — single-repo projects, or rows
126+
the plugin has not processed. Dashboards render these as the single implicit group `All`
127+
via `COALESCE(sub_project, 'All')`.
128+
129+
Attribution is rebuilt from scratch on every run, so changing the configuration and
130+
re-running the blueprint reclassifies existing data.
131+
132+
:::note
133+
The plugin also populates `monorepo_subproject_deployments` and
134+
`monorepo_subproject_pr_metrics`. Both are deprecated and will be removed in a future
135+
release — query the tables above instead.
136+
:::
137+
138+
## Dashboard
139+
140+
A built-in Grafana dashboard, **Monorepo Sub-Projects**, visualises the output:
141+
142+
- Deployment Count by Sub-Project
143+
- Deployment Frequency over Time
144+
- Change Lead Time Breakdown by Sub-Project
145+
- Unattributed PRs
146+
- Awaiting Deployment
147+
- Attributed Deployments

0 commit comments

Comments
 (0)