Skip to content

Commit c3f771d

Browse files
Merge pull request #83 from VirdocsSoftware/release/v2.13.0
release v2.13.0 to main
2 parents 93bafd2 + feb4e46 commit c3f771d

5 files changed

Lines changed: 230 additions & 13 deletions

File tree

.github/actions/static-analysis/action.yml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,46 @@ inputs:
77
required: false
88
default: "actions,VirdocsSoftware"
99

10+
layer-package-json:
11+
description: "Path to the layer package.json file"
12+
required: true
13+
14+
domains:
15+
description: "Comma-separated list of domains to analyze"
16+
required: true
17+
1018
runs:
1119
using: "composite"
1220
steps:
1321
- name: Checkout Code
1422
uses: actions/checkout@v4
23+
continue-on-error: true
24+
1525
- name: Scan workflow yml files
26+
id: scan-workflows
1627
run: |
1728
echo "Running the following script: ${{ github.action_path }}/scan_github_actions.js"
1829
echo "With the current working directory: $(pwd)"
1930
node ${{ github.action_path }}/scan_github_actions.js
2031
shell: bash
2132
working-directory: ${{ github.workspace }}
2233
env:
23-
IGNORED_ACCOUNTS: ${{ inputs.ignored-accounts }}
34+
IGNORED_ACCOUNTS: ${{ inputs.ignored-accounts }}
35+
continue-on-error: true
36+
37+
- name: Run layer dependency analysis
38+
id: layer-dependency-analysis
39+
run: |
40+
echo "Running layer dependency analysis script"
41+
node ${{ github.action_path }}/layer_dependency_analysis.js "${{ inputs.layer-package-json }}" '${{ inputs.domains }}'
42+
shell: bash
43+
working-directory: ${{ github.workspace }}
44+
continue-on-error: true
45+
46+
- name: Check results
47+
run: |
48+
if [ "${{ steps.scan-workflows.outcome }}" != "success" ] || [ "${{ steps.layer-dependency-analysis.outcome }}" != "success" ]; then
49+
echo "One or more steps failed"
50+
exit 1
51+
fi
52+
shell: bash
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
const fs = require('fs');
2+
3+
class PackageJsonDependencyComparator {
4+
/**
5+
* Compares dependencies between two package.json files
6+
* @param {Object} packageJson1 - First package.json object
7+
* @param {Object} packageJson2 - Second package.json object
8+
* @returns {Object} Report containing mismatched dependencies
9+
*/
10+
compareDependencies(packageJson1, packageJson2) {
11+
const report = {
12+
mismatches: [],
13+
missingInFirst: [],
14+
missingInSecond: []
15+
};
16+
console.log('Comparing dependencies:', packageJson1, packageJson2);
17+
18+
// Get all dependencies from both files
19+
const deps1 = this._getAllDependencies(packageJson1);
20+
const deps2 = this._getAllDependencies(packageJson2);
21+
console.log('Dependencies:', deps1, deps2);
22+
23+
// Compare dependencies
24+
for (const [dep, version1] of Object.entries(deps1)) {
25+
const version2 = deps2[dep];
26+
console.log('Comparing dependency:', dep, version1, version2);
27+
28+
if (version2 === undefined) {
29+
continue; // we don't care about missing dependencies
30+
} else if (version1 !== version2) {
31+
report.mismatches.push({
32+
dependency: dep,
33+
version1,
34+
version2
35+
});
36+
}
37+
}
38+
39+
// Find dependencies that exist only in second file
40+
for (const [dep] of Object.entries(deps2)) {
41+
if (deps1[dep] === undefined) {
42+
continue; // we don't care about missing dependencies
43+
}
44+
}
45+
46+
return report;
47+
}
48+
49+
/**
50+
* Extracts all dependencies from a package.json object
51+
* @param {Object} packageJson - package.json object
52+
* @returns {Object} Combined dependencies object
53+
*/
54+
_getAllDependencies(packageJson) {
55+
return {
56+
...packageJson.dependencies || {},
57+
...packageJson.devDependencies || {},
58+
...packageJson.peerDependencies || {},
59+
...packageJson.optionalDependencies || {}
60+
};
61+
}
62+
63+
/**
64+
* Formats the comparison report into a readable string
65+
* @param {Object} report - Comparison report
66+
* @returns {string} Formatted report
67+
*/
68+
formatReport(report) {
69+
let parts = [];
70+
71+
if (report.mismatches.length > 0) {
72+
const mismatchItems = report.mismatches.map(({ dependency, version1, version2 }) =>
73+
`${dependency}:${version1}vs${version2}`
74+
).join(', ');
75+
parts.push(`Mismatched against layer: [${mismatchItems}]`);
76+
}
77+
78+
if (report.missingInFirst.length > 0) {
79+
const missingFirstItems = report.missingInFirst.map(({ dependency, version }) =>
80+
`${dependency}:${version}`
81+
).join(', ');
82+
parts.push(`Missing in First: [${missingFirstItems}]`);
83+
}
84+
85+
if (report.missingInSecond.length > 0) {
86+
const missingSecondItems = report.missingInSecond.map(({ dependency, version }) =>
87+
`${dependency}:${version}`
88+
).join(', ');
89+
parts.push(`Missing in Second: [${missingSecondItems}]`);
90+
}
91+
92+
return parts.length > 0 ? parts.join(' | ') : 'No differences found between package.json files.';
93+
}
94+
}
95+
96+
class LayerDependencyAnalysis {
97+
constructor(comparator) {
98+
this.comparator = comparator;
99+
}
100+
101+
run(layerPackageJson, domainPackageJsons) {
102+
console.log('Running layer dependency analysis');
103+
104+
const reports = domainPackageJsons.map(domainPackageJson => {
105+
return {
106+
project: domainPackageJson.project,
107+
report: this.comparator.compareDependencies(layerPackageJson, domainPackageJson.packageJson)
108+
};
109+
});
110+
111+
const reportsWithWarnings = reports.filter(report => report.report.mismatches.length > 0);
112+
113+
if (reportsWithWarnings.length > 0) {
114+
console.log('Reports with mismatched dependencies:');
115+
reportsWithWarnings.forEach(report => {
116+
// output warning to github actions
117+
console.log(`::warning file=${report.project}/package.json::${this.comparator.formatReport(report.report)}`);
118+
});
119+
} else {
120+
console.log('No mismatched dependencies found');
121+
}
122+
}
123+
}
124+
125+
function main() {
126+
if (process.argv.length < 4) {
127+
console.error('Usage: node layer_dependency_analysis.js <layer-package-json> <domains>');
128+
process.exit(1);
129+
}
130+
131+
console.log('Layer package.json:', process.argv[2]);
132+
console.log('Domains:', process.argv[3]);
133+
console.log('Current working directory:', process.cwd());
134+
135+
// Example usage:
136+
const comparator = new PackageJsonDependencyComparator();
137+
138+
const layerDependencyAnalysis = new LayerDependencyAnalysis(comparator);
139+
140+
console.log('Reading layer package.json:', process.cwd() + '/' + process.argv[2]);
141+
const layerPackageJson = JSON.parse(fs.readFileSync(process.cwd() + '/' + process.argv[2], 'utf8'));
142+
const domains = JSON.parse(process.argv[3]); // {"include": [{"project": "domain1"}, {"project": "domain2"}]}
143+
144+
const domainPackageJsons = domains.include.filter(domain => domain.project != '.').map(domain => {
145+
return {
146+
project: domain.project,
147+
packageJson: JSON.parse(fs.readFileSync(process.cwd() + '/domains/' + domain.project + '/package.json', 'utf8'))
148+
};
149+
});
150+
151+
// print the test plan
152+
console.log('Test plan:');
153+
console.log('Layer package.json:', layerPackageJson);
154+
console.log('Domains:', domains);
155+
console.log('Domain package.json:', JSON.stringify(domainPackageJsons, null, 2));
156+
157+
layerDependencyAnalysis.run(layerPackageJson, domainPackageJsons);
158+
}
159+
160+
main();
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "static-analysis",
3+
"version": "1.0.0"
4+
}

.github/actions/static-analysis/scan_github_actions.js

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,45 @@ class StaticAnalysis {
4444

4545
run() {
4646
const workflowDir = this.dataProvider.path.join(this.process.cwd(), '.github', 'workflows');
47-
if (!this.dataProvider.fileExists(workflowDir)) {
48-
console.error(`Error: Directory ${workflowDir} does not exist.`);
49-
this.process.exit(1);
50-
}
51-
52-
const yamlFiles = this.findYamlFiles(workflowDir);
47+
const domainsDir = this.dataProvider.path.join(this.process.cwd(), 'domains');
5348
let allWarnings = [];
5449

55-
yamlFiles.forEach(filePath => {
56-
const warnings = this.scanFile(filePath);
57-
allWarnings = allWarnings.concat(warnings);
58-
});
50+
// Scan .github/workflows directory if it exists
51+
if (this.dataProvider.fileExists(workflowDir)) {
52+
const yamlFiles = this.findYamlFiles(workflowDir);
53+
yamlFiles.forEach(filePath => {
54+
const warnings = this.scanFile(filePath);
55+
allWarnings = allWarnings.concat(warnings);
56+
});
57+
}
58+
59+
// Scan domains/*/.github/**/*.yml files if domains directory exists
60+
if (this.dataProvider.fileExists(domainsDir)) {
61+
const domains = this.dataProvider.readDirectory(domainsDir);
62+
domains.forEach(domain => {
63+
const domainPath = this.dataProvider.path.join(domainsDir, domain);
64+
const domainGithubPath = this.dataProvider.path.join(domainPath, '.github');
65+
66+
if (this.dataProvider.fileExists(domainGithubPath)) {
67+
const yamlFiles = this.findYamlFiles(domainGithubPath);
68+
yamlFiles.forEach(filePath => {
69+
const warnings = this.scanFile(filePath);
70+
allWarnings = allWarnings.concat(warnings);
71+
});
72+
}
73+
});
74+
}
5975

6076
if (allWarnings.length > 0) {
61-
allWarnings.forEach(warning => console.warn(warning));
77+
allWarnings.forEach(warning => {
78+
// Extract file path and line number if available
79+
const fileMatch = warning.match(/In file (.*?),/);
80+
const filePath = fileMatch ? fileMatch[1] : '';
81+
const relativePath = filePath ? this.dataProvider.path.relative(this.process.cwd(), filePath) : '';
82+
83+
// Output warning using GitHub's Workflow Commands
84+
console.log(`::warning file=${relativePath}::${warning}`);
85+
});
6286
console.log('To fix these issues, refer to the solution in the following Jira ticket: https://virdocs.atlassian.net/browse/RD-2964');
6387
this.process.exit(0); // TODO: Exit with non-zero code if warnings are found
6488
} else {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "github-actions",
3-
"version": "2.12.0",
3+
"version": "2.13.0",
44
"description": "Used to store GitHub actions for use across the enterprise",
55
"scripts": {
66
"test": "./tooling/scripts/run_tests.sh",

0 commit comments

Comments
 (0)