Skip to content

Commit c8547d9

Browse files
authored
Build: Detect circular dependencies with Madge (#17924)
* feat: adds script to run madge in a CI environment * build: adds check for circular dependencies * build: move actions higher up * build: only print annotation once * build: make script not fail CI until dependencies are fixed
1 parent 1c468e2 commit c8547d9

File tree

3 files changed

+78
-1
lines changed

3 files changed

+78
-1
lines changed

.github/workflows/test-backoffice.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ jobs:
4040
cache: npm
4141
cache-dependency-path: ./src/Umbraco.Web.UI.Client/package-lock.json
4242
- run: npm ci --no-audit --no-fund --prefer-offline
43+
- name: Check for circular dependencies
44+
run: node devops/circular/index.js src
4345
- run: npm run lint:errors
4446
- run: npm run build:for:cms
4547
- run: npm run check:paths
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* This module is used to detect circular dependencies in the Umbraco backoffice.
3+
* It is used in the build process to ensure that we don't have circular dependencies.
4+
* @example node devops/circular/index.js src
5+
* @author Umbraco HQ
6+
*/
7+
8+
import madge from 'madge';
9+
import { join } from 'path';
10+
import { mkdirSync } from 'fs';
11+
12+
const __dirname = import.meta.dirname;
13+
const IS_GITHUB_ACTIONS = process.env.GITHUB_ACTIONS === 'true';
14+
const IS_AZURE_PIPELINES = process.env.TF_BUILD === 'true';
15+
const baseDir = process.argv[2] || 'src';
16+
const specificPaths = (process.argv[3] || '').split(',');
17+
18+
console.log('Scanning for circular dependencies in:', baseDir);
19+
20+
const madgeSetup = await madge(specificPaths, {
21+
baseDir,
22+
fileExtensions: ['ts'],
23+
tsConfig: join(baseDir, 'tsconfig.build.json'),
24+
detectiveOptions: {
25+
ts: {
26+
skipTypeImports: true,
27+
skipAsyncImports: true
28+
}
29+
}
30+
});
31+
32+
console.log('-'.repeat(80));
33+
34+
const circular = madgeSetup.circular();
35+
36+
if (circular.length) {
37+
console.error(circular.length, 'circular dependencies detected:\n');
38+
for (let i = 0; i < circular.length; i++) {
39+
printCircularDependency(circular[i], i + 1);
40+
}
41+
console.error('\nPlease fix the circular dependencies before proceeding.\n');
42+
43+
try {
44+
const imagePath = join(__dirname, '../../madge');
45+
mkdirSync(imagePath, { recursive: true });
46+
const image = await madgeSetup.image(join(imagePath, 'circular.svg'), true);
47+
console.log('Circular dependencies graph generated:', image);
48+
} catch { console.warn('No image generated. Make sure Graphviz is in your $PATH if you want a visualization'); }
49+
50+
// TODO: Set this to 1 when we have fixed all circular dependencies
51+
process.exit(0);
52+
}
53+
54+
console.log('\nNo circular dependencies detected.\n');
55+
process.exit(0);
56+
57+
/**
58+
*
59+
* @param {string[]} circular The circular dependencies.
60+
* @param {number} idx The index of the circular dependency.
61+
*/
62+
function printCircularDependency(circular, idx) {
63+
circular = circular.map(file => `${baseDir}/${file}`);
64+
const circularPath = circular.join(' -> ');
65+
66+
if (IS_GITHUB_ACTIONS) {
67+
console.error(`::error file=${circular[0]},title=Circular dependency::Circular dependencies detected: ${circularPath}`);
68+
}
69+
else if (IS_AZURE_PIPELINES) {
70+
console.error(`##vso[task.logissue type=error;sourcepath=${circular[0]};]Circular dependencies detected: ${circularPath}`);
71+
} else {
72+
console.error(idx, '=', circularPath, '\n');
73+
}
74+
75+
}

src/Umbraco.Web.UI.Client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@
158158
"postbuild": "rollup -c ./src/rollup.config.js && node ./devops/build/global-types.js",
159159
"check": "npm run lint:errors && npm run compile && npm run build-storybook && npm run generate:jsonschema:dist",
160160
"check:paths": "node ./devops/build/check-path-length.js dist-cms 120",
161-
"check:circular": "madge --circular --warning --extensions ts ./src",
161+
"check:circular": "node ./devops/circular/index.js src",
162162
"compile": "tsc",
163163
"dev": "vite",
164164
"dev:server": "cross-env VITE_UMBRACO_USE_MSW=off vite",

0 commit comments

Comments
 (0)