Skip to content

Commit 74c5874

Browse files
committed
#RIVS-254 - Automatically update the file with dependences
1 parent 3a8180c commit 74c5874

File tree

4 files changed

+387
-6
lines changed

4 files changed

+387
-6
lines changed

.circleci/config.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,27 @@ jobs:
329329
330330
aws s3 cp release/ s3://${AWS_BUCKET_NAME}/private/vscode/${applicationVersion} --recursive
331331
332+
licenses-check:
333+
executor: linux-executor
334+
steps:
335+
- checkout
336+
- restore_cache:
337+
<<: *uiDepsCacheKey
338+
- run:
339+
name: Run install all dependencies
340+
command: |
341+
yarn install
342+
yarn --cwd tests/e2e install
343+
- run:
344+
name: Generate licenses csv files and send csv data to google sheet
345+
command: |
346+
npm i -g license-checker
347+
348+
echo "$GOOGLE_ACCOUNT_SERVICE_KEY_BASE64" | base64 -id > gasKey.json
349+
SPREADSHEET_ID=$GOOGLE_SPREADSHEET_DEPENDENCIES_ID node .circleci/deps-licenses-report.js
350+
- store_artifacts:
351+
path: licenses
352+
destination: licenses
332353

333354
# Orchestrate jobs using workflows
334355
# See: https://circleci.com/docs/configuration-reference/#workflows
@@ -539,3 +560,16 @@ workflows:
539560
# - Approve Publish Release (prod)
540561
# <<: *prodFilter # double check for "latest"
541562

563+
weekly:
564+
triggers:
565+
- schedule:
566+
cron: '0 0 * * 1'
567+
filters:
568+
branches:
569+
only:
570+
- main
571+
jobs:
572+
# Process all licenses
573+
- licenses-check:
574+
name: Process licenses of packages
575+

.circleci/deps-licenses-report.js

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
const fs = require('fs');
2+
const { join } = require('path');
3+
const { last, set } = require('lodash');
4+
const { google } = require('googleapis');
5+
const { exec } = require('child_process');
6+
const csvParser = require('csv-parser');
7+
const { stringify } = require('csv-stringify');
8+
9+
const licenseFolderName = 'licenses';
10+
const spreadsheetId = process.env.SPREADSHEET_ID;
11+
const summaryFilePath = `./${licenseFolderName}/summary.csv`;
12+
const allData = [];
13+
let csvFiles = [];
14+
15+
// Main function
16+
async function main() {
17+
const folderPath = './';
18+
const packageJsons = findPackageJsonFiles(folderPath); // Find all package.json files in the given folder
19+
20+
console.log('All package.jsons was found:', packageJsons);
21+
22+
// Create the folder if it doesn't exist
23+
if (!fs.existsSync(licenseFolderName)) {
24+
fs.mkdirSync(licenseFolderName);
25+
}
26+
27+
try {
28+
await Promise.all(packageJsons.map(runLicenseCheck));
29+
console.log('All csv files was generated');
30+
await generateSummary()
31+
await sendLicensesToGoogleSheet()
32+
} catch (error) {
33+
console.error('An error occurred:', error);
34+
process.exit(1);
35+
}
36+
}
37+
38+
main();
39+
40+
// Function to find all package.json files in a given folder
41+
function findPackageJsonFiles(folderPath) {
42+
const packageJsonPaths = [];
43+
const packageJsonName = 'package.json';
44+
const excludeFolders = ['dist', 'node_modules', 'test-extensions'];
45+
46+
// Recursive function to search for package.json files
47+
function searchForPackageJson(currentPath) {
48+
const files = fs.readdirSync(currentPath);
49+
50+
for (const file of files) {
51+
const filePath = join(currentPath, file);
52+
const stats = fs.statSync(filePath);
53+
54+
if (stats.isDirectory() && !excludeFolders.includes(file)) {
55+
searchForPackageJson(filePath);
56+
} else if (file === packageJsonName) {
57+
packageJsonPaths.push(`./${filePath.slice(0, -packageJsonName.length - 1)}`);
58+
}
59+
}
60+
}
61+
62+
searchForPackageJson(folderPath);
63+
return packageJsonPaths;
64+
}
65+
66+
// Function to run license check for a given package.json file
67+
async function runLicenseCheck(path) {
68+
const name = last(path.split('/')) || 'vscode';
69+
70+
const COMMANDS = [
71+
`license-checker --start ${path} --csv --out ./${licenseFolderName}/${name}_prod.csv --production`,
72+
`license-checker --start ${path} --csv --out ./${licenseFolderName}/${name}_dev.csv --development`,
73+
]
74+
75+
return await Promise.all(COMMANDS.map((command) =>
76+
new Promise((resolve, reject) => {
77+
exec(command, (error, stdout, stderr) => {
78+
if (error) {
79+
console.error(`Failed command: ${command}, error:`, stderr);
80+
reject(error);
81+
}
82+
resolve();
83+
});
84+
})
85+
));
86+
}
87+
88+
async function sendLicensesToGoogleSheet() {
89+
try {
90+
const serviceAccountKey = JSON.parse(fs.readFileSync('./gasKey.json', 'utf-8'));
91+
92+
// Set up JWT client
93+
const jwtClient = new google.auth.JWT(
94+
serviceAccountKey.client_email,
95+
null,
96+
serviceAccountKey.private_key,
97+
['https://www.googleapis.com/auth/spreadsheets']
98+
);
99+
100+
const sheets = google.sheets('v4');
101+
102+
// Read all .csv files in the 'licenses' folder
103+
csvFiles.forEach((csvFile) => {
104+
// Extract sheet name from file name
105+
const sheetName = csvFile.replace('.csv', '').replaceAll('_', ' ');
106+
107+
const data = [];
108+
fs.createReadStream(`./${licenseFolderName}/${csvFile}`)
109+
.pipe(csvParser({ headers: false }))
110+
.on('data', (row) => {
111+
data.push(Object.values(row));
112+
})
113+
.on('end', async () => {
114+
const resource = { values: data };
115+
116+
try {
117+
const response = await sheets.spreadsheets.get({
118+
auth: jwtClient,
119+
spreadsheetId,
120+
});
121+
122+
const sheet = response.data.sheets.find((sheet) => sheet.properties.title === sheetName);
123+
if (sheet) {
124+
// Clear contents of the sheet starting from cell A2
125+
await sheets.spreadsheets.values.clear({
126+
auth: jwtClient,
127+
spreadsheetId,
128+
range: `${sheetName}!A1:Z`, // Assuming Z is the last column
129+
});
130+
} else {
131+
// Create the sheet if it doesn't exist
132+
await sheets.spreadsheets.batchUpdate({
133+
auth: jwtClient,
134+
spreadsheetId,
135+
resource: set({}, 'requests[0].addSheet.properties.title', sheetName),
136+
});
137+
}
138+
} catch (error) {
139+
console.error(`Error checking/creating sheet for ${sheetName}:`, error);
140+
}
141+
142+
try {
143+
await sheets.spreadsheets.values.batchUpdate({
144+
auth: jwtClient,
145+
spreadsheetId,
146+
resource: {
147+
valueInputOption: 'RAW',
148+
data: [
149+
{
150+
range: `${sheetName}!A1`, // Use the sheet name as the range and start from A2
151+
majorDimension: 'ROWS',
152+
values: data,
153+
},
154+
],
155+
},
156+
});
157+
158+
console.log(`CSV data has been inserted into ${sheetName} sheet.`);
159+
} catch (err) {
160+
console.error(`Error inserting data for ${sheetName}:`, err);
161+
}
162+
});
163+
});
164+
} catch (error) {
165+
console.error('Error loading service account key:', error);
166+
}
167+
}
168+
169+
// Function to read and process each CSV file
170+
const processCSVFile = (file) => {
171+
return new Promise((resolve, reject) => {
172+
const parser = csvParser({ columns: true, trim: true });
173+
const input = fs.createReadStream(`./${licenseFolderName}/${file}`);
174+
175+
parser.on('data', (record) => {
176+
allData.push(record);
177+
});
178+
179+
parser.on('end', () => {
180+
resolve();
181+
});
182+
183+
parser.on('error', (err) => {
184+
reject(err);
185+
});
186+
187+
input.pipe(parser);
188+
});
189+
};
190+
191+
// Process and aggregate license data
192+
const processLicenseData = () => {
193+
const licenseCountMap = {};
194+
for (const record of allData) {
195+
const license = record.license;
196+
licenseCountMap[license] = (licenseCountMap[license] || 0) + 1;
197+
}
198+
return licenseCountMap;
199+
};
200+
201+
// Create summary CSV data
202+
const createSummaryData = (licenseCountMap) => {
203+
const summaryData = [['License', 'Count']];
204+
for (const license in licenseCountMap) {
205+
summaryData.push([license, licenseCountMap[license]]);
206+
}
207+
return summaryData;
208+
};
209+
210+
// Write summary CSV file
211+
const writeSummaryCSV = async (summaryData) => {
212+
try {
213+
const summaryCsvString = await stringifyPromise(summaryData);
214+
fs.writeFileSync(summaryFilePath, summaryCsvString);
215+
csvFiles.push(last(summaryFilePath.split('/')));
216+
console.log(`Summary CSV saved as ${summaryFilePath}`);
217+
} catch (err) {
218+
console.error(`Error: ${err}`);
219+
}
220+
};
221+
222+
// Stringify as a promise
223+
const stringifyPromise = (data) => {
224+
return new Promise((resolve, reject) => {
225+
stringify(data, (err, csvString) => {
226+
if (err) {
227+
reject(err);
228+
} else {
229+
resolve(csvString);
230+
}
231+
});
232+
});
233+
};
234+
235+
async function generateSummary() {
236+
csvFiles = fs.readdirSync(licenseFolderName).filter(file => file.endsWith('.csv')).sort();
237+
238+
for (const file of csvFiles) {
239+
try {
240+
await processCSVFile(file);
241+
} catch (err) {
242+
console.error(`Error processing ${file}: ${err}`);
243+
}
244+
}
245+
246+
const licenseCountMap = processLicenseData();
247+
const summaryData = createSummaryData(licenseCountMap);
248+
249+
await writeSummaryCSV(summaryData);
250+
}

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
"l10n": "./l10n",
99
"publisher": "Redis",
1010
"pricing": "Free",
11-
"categories": ["Visualization", "Programming Languages"],
11+
"categories": [
12+
"Visualization",
13+
"Programming Languages"
14+
],
1215
"icon": "dist/webviews/resources/256x256.png",
1316
"author": {
1417
"name": "Redis Ltd.",
@@ -209,6 +212,8 @@
209212
"@vscode/vsce": "^3.0.0",
210213
"autoprefixer": "^10.4.16",
211214
"cross-env": "^7.0.3",
215+
"csv-parser": "^3.0.0",
216+
"csv-stringify": "^6.5.1",
212217
"esbuild": "^0.19.5",
213218
"eslint": "^7.32.0",
214219
"eslint-config-airbnb": "^18.2.1",
@@ -225,6 +230,7 @@
225230
"eslint-plugin-sonarjs": "^0.23.0",
226231
"eslint-plugin-storybook": "^0.6.15",
227232
"eslint-plugin-tailwindcss": "^3.15.1",
233+
"googleapis": "^142.0.0",
228234
"html-entities": "^2.3.2",
229235
"husky": "^8.0.3",
230236
"identity-obj-proxy": "^3.0.0",

0 commit comments

Comments
 (0)