mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-07 14:02:48 +00:00
refactor: adjust code to updated dependencies
This commit is contained in:
123
package-lock.json
generated
123
package-lock.json
generated
@@ -22,14 +22,6 @@
|
||||
"chalk": "^2.0.0",
|
||||
"esutils": "^2.0.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/runtime": {
|
||||
@@ -47,33 +39,62 @@
|
||||
"integrity": "sha512-PbDyjVme3HR8CrMI04SokU97Enq/+txP5fS2O0XYVSmMYteJ7Q9CLO2y0t8PmNZkt4YCxmHgaNEdMs+/Ki+PAA=="
|
||||
},
|
||||
"@openstapps/configuration": {
|
||||
"version": "0.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@openstapps/configuration/-/configuration-0.16.1.tgz",
|
||||
"integrity": "sha512-GEYYfL0do3jikl2UyfvNdGJoQZGeo9sCYkfDrCsOYDZNxuHkHq5fzOPx8OJtMLblNzLgN65tiW+JPRWw6wTwKg==",
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@openstapps/configuration/-/configuration-0.18.0.tgz",
|
||||
"integrity": "sha512-Ufi3jzCozVqCymNeaeRzuOHO2Yd5qXJ10uF4xNHk6Q4LFD9NAMMBkYbawkjmecZoNR+Llqs4AnwSxIkuEAxcxA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "10.14.4",
|
||||
"@types/node": "10.14.7",
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/yaml": "1.0.2",
|
||||
"chalk": "2.4.2",
|
||||
"commander": "2.20.0",
|
||||
"semver": "6.0.0",
|
||||
"semver": "6.1.0",
|
||||
"tslint": "5.16.0",
|
||||
"tslint-eslint-rules": "5.4.0",
|
||||
"yaml": "1.5.0"
|
||||
"yaml": "1.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "10.14.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.4.tgz",
|
||||
"integrity": "sha512-DT25xX/YgyPKiHFOpNuANIQIVvYEwCWXgK2jYYwqgaMrYE6+tq+DtmMwlD3drl6DJbUwtlIDnn0d7tIn/EbXBg==",
|
||||
"version": "10.14.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.7.tgz",
|
||||
"integrity": "sha512-on4MmIDgHXiuJDELPk1NFaKVUxxCFr37tm8E9yN6rAiF5Pzp/9bBfBHkoexqRiY+hk/Z04EJU9kKEb59YqJ82A==",
|
||||
"dev": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz",
|
||||
"integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.1.0.tgz",
|
||||
"integrity": "sha512-kCqEOOHoBcFs/2Ccuk4Xarm/KiWRSLEX9CAZF8xkJ6ZPlIoTZ8V5f7J16vYLJqDbR7KrxTJpR2lqjIEm2Qx9cQ==",
|
||||
"dev": true
|
||||
},
|
||||
"tslint": {
|
||||
"version": "5.16.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.16.0.tgz",
|
||||
"integrity": "sha512-UxG2yNxJ5pgGwmMzPMYh/CCnCnh0HfPgtlVRDs1ykZklufFBL1ZoTlWFRz2NQjcoEiDoRp+JyT0lhBbbH/obyA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"builtin-modules": "^1.1.1",
|
||||
"chalk": "^2.3.0",
|
||||
"commander": "^2.12.1",
|
||||
"diff": "^3.2.0",
|
||||
"glob": "^7.1.1",
|
||||
"js-yaml": "^3.13.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"mkdirp": "^0.5.1",
|
||||
"resolve": "^1.3.2",
|
||||
"semver": "^5.3.0",
|
||||
"tslib": "^1.8.0",
|
||||
"tsutils": "^2.29.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.7.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
|
||||
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -115,26 +136,26 @@
|
||||
}
|
||||
},
|
||||
"@openstapps/logger": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@openstapps/logger/-/logger-0.1.0.tgz",
|
||||
"integrity": "sha512-5z7Yf3WrzayEVNPp1TBoGiCVgPlQtqzOFh0yQ06gac/vFedWLPLBmENGDdRoEKar8bXzghkxDLy6Rvj/8HEQaQ==",
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@openstapps/logger/-/logger-0.2.1.tgz",
|
||||
"integrity": "sha512-6+F1nxEBuNTrd3hhBxKnvkH8B84HvB/dVmoMP9Pmv2g3mL3pYJ9l2BBGaACDRA7oUCyLpbNQw+4Kf+VdyzOttw==",
|
||||
"requires": {
|
||||
"@types/node": "10.14.6",
|
||||
"@types/nodemailer": "4.6.8",
|
||||
"@types/node": "10.14.7",
|
||||
"@types/nodemailer": "6.1.0",
|
||||
"chalk": "2.4.2",
|
||||
"flatted": "2.0.0",
|
||||
"nodemailer": "6.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "10.14.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz",
|
||||
"integrity": "sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg=="
|
||||
"version": "10.14.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.7.tgz",
|
||||
"integrity": "sha512-on4MmIDgHXiuJDELPk1NFaKVUxxCFr37tm8E9yN6rAiF5Pzp/9bBfBHkoexqRiY+hk/Z04EJU9kKEb59YqJ82A=="
|
||||
},
|
||||
"@types/nodemailer": {
|
||||
"version": "4.6.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-4.6.8.tgz",
|
||||
"integrity": "sha512-IX1P3bxDP1VIdZf6/kIWYNmSejkYm9MOyMEtoDFi4DVzKjJ3kY4GhOcOAKs6lZRjqVVmF9UjPOZXuQczlpZThw==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.1.0.tgz",
|
||||
"integrity": "sha512-WysSJ4sGW2Aum1Cs6HFosZdlR3WUzX0XoSLsI53q77gLd4wDfE84OXffZu5/nLIjeKh4SwfTsdrKsgBL9WowMA==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -314,9 +335,9 @@
|
||||
"integrity": "sha512-RTVWV485OOf4+nO2+feurk0chzHkSjkjALiejpHltyuMf/13fGymbbNNFrSKdSSUg1TIwzszXdWsVirxgqYiFA=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "10.14.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.7.tgz",
|
||||
"integrity": "sha512-on4MmIDgHXiuJDELPk1NFaKVUxxCFr37tm8E9yN6rAiF5Pzp/9bBfBHkoexqRiY+hk/Z04EJU9kKEb59YqJ82A=="
|
||||
"version": "10.14.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.8.tgz",
|
||||
"integrity": "sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw=="
|
||||
},
|
||||
"@types/nodemailer": {
|
||||
"version": "4.6.5",
|
||||
@@ -1535,6 +1556,12 @@
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.13.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
||||
@@ -2479,9 +2506,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"tslint": {
|
||||
"version": "5.16.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.16.0.tgz",
|
||||
"integrity": "sha512-UxG2yNxJ5pgGwmMzPMYh/CCnCnh0HfPgtlVRDs1ykZklufFBL1ZoTlWFRz2NQjcoEiDoRp+JyT0lhBbbH/obyA==",
|
||||
"version": "5.17.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.17.0.tgz",
|
||||
"integrity": "sha512-pflx87WfVoYepTet3xLfDOLDm9Jqi61UXIKePOuca0qoAZyrGWonDG9VTbji58Fy+8gciUn8Bt7y69+KEVjc/w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
@@ -2490,7 +2517,7 @@
|
||||
"commander": "^2.12.1",
|
||||
"diff": "^3.2.0",
|
||||
"glob": "^7.1.1",
|
||||
"js-yaml": "^3.13.0",
|
||||
"js-yaml": "^3.13.1",
|
||||
"minimatch": "^3.0.4",
|
||||
"mkdirp": "^0.5.1",
|
||||
"resolve": "^1.3.2",
|
||||
@@ -2517,9 +2544,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"tsutils": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.10.0.tgz",
|
||||
"integrity": "sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==",
|
||||
"version": "3.13.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.13.0.tgz",
|
||||
"integrity": "sha512-wRtEjVU8Su72sDIDoqno5Scwt8x4eaF0teKO3m4hu8K1QFPnIZMM88CLafs2tapUeWnY9SwwO3bWeOt2uauBcg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.8.1"
|
||||
@@ -2590,9 +2617,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.4.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz",
|
||||
"integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==",
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.1.tgz",
|
||||
"integrity": "sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw==",
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
@@ -2678,12 +2705,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"yaml": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.5.0.tgz",
|
||||
"integrity": "sha512-nKxSWOa7vxAP2pikrGxbkZsG/garQseRiLn9mIDjzwoQsyVy7ZWIpLoARejnINGGLA4fttuzRFFNxxbsztdJgw==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.6.0.tgz",
|
||||
"integrity": "sha512-iZfse3lwrJRoSlfs/9KQ9iIXxs9++RvBFVzAqbbBiFT+giYtyanevreF9r61ZTbGMgWQBxAua3FzJiniiJXWWw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.3"
|
||||
"@babel/runtime": "^7.4.5"
|
||||
}
|
||||
},
|
||||
"yn": {
|
||||
|
||||
15
package.json
15
package.json
@@ -12,8 +12,11 @@
|
||||
"check-configuration": "openstapps-configuration",
|
||||
"compile": "rimraf lib && tsc && prepend lib/cli.js '#!/usr/bin/env node\n'",
|
||||
"documentation": "typedoc --includeDeclarations --mode modules --out docs --readme README.md --listInvalidSymbolLinks src",
|
||||
"postversion": "npm run changelog",
|
||||
"prepublishOnly": "npm ci && npm run build",
|
||||
"tslint": "tslint 'src/**/*.ts'"
|
||||
"preversion": "npm run prepublishOnly",
|
||||
"push": "git push && git push origin \"v$npm_package_version\"",
|
||||
"tslint": "tslint -p tsconfig.json -c tslint.json 'src/**/*.ts'"
|
||||
},
|
||||
"author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>",
|
||||
"contributors": [
|
||||
@@ -25,25 +28,25 @@
|
||||
"dependencies": {
|
||||
"@krlwlfrt/async-pool": "0.1.0",
|
||||
"@openstapps/gitlab-api": "0.5.1",
|
||||
"@openstapps/logger": "0.1.0",
|
||||
"@openstapps/logger": "0.2.1",
|
||||
"@slack/client": "5.0.1",
|
||||
"@types/glob": "7.1.1",
|
||||
"@types/mustache": "0.8.32",
|
||||
"@types/node": "10.14.7",
|
||||
"@types/node": "10.14.8",
|
||||
"commander": "2.20.0",
|
||||
"glob": "7.1.4",
|
||||
"moment": "2.24.0",
|
||||
"mustache": "3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openstapps/configuration": "0.16.1",
|
||||
"@openstapps/configuration": "0.18.0",
|
||||
"conventional-changelog-cli": "2.0.21",
|
||||
"prepend-file-cli": "1.0.6",
|
||||
"rimraf": "2.6.3",
|
||||
"ts-node": "8.2.0",
|
||||
"tslint": "5.16.0",
|
||||
"tslint": "5.17.0",
|
||||
"typedoc": "0.14.2",
|
||||
"typescript": "3.4.5"
|
||||
"typescript": "3.5.1"
|
||||
},
|
||||
"main": "lib/common.js",
|
||||
"typings": "lib/common.d.ts",
|
||||
|
||||
@@ -23,13 +23,14 @@ import {tidy} from './tasks/tidy';
|
||||
import {unlabel} from './tasks/unlabel';
|
||||
|
||||
// add default handler for unhandled rejections
|
||||
process.on('unhandledRejection', (err) => {
|
||||
Logger.error('UNHANDLED REJECTION', err.stack);
|
||||
process.on('unhandledRejection', async (err) => {
|
||||
await Logger.error('UNHANDLED REJECTION', err.stack);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// check that environment variable GITLAB_PRIVATE_TOKEN is set
|
||||
if (typeof process.env.GITLAB_PRIVATE_TOKEN !== 'string' || process.env.GITLAB_PRIVATE_TOKEN.length === 0) {
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
Logger.error('Environment variable GITLAB_PRIVATE_TOKEN is not set!');
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -37,7 +38,8 @@ if (typeof process.env.GITLAB_PRIVATE_TOKEN !== 'string' || process.env.GITLAB_P
|
||||
const gitlabApi = new Api(GITLAB_API_URL, process.env.GITLAB_PRIVATE_TOKEN as string);
|
||||
|
||||
if (existsSync(join(__dirname, 'package.json'))) {
|
||||
commander.version(JSON.parse(readFileSync(join(__dirname, '..', 'package.json')).toString()).version);
|
||||
commander.version(JSON.parse(readFileSync(join(__dirname, '..', 'package.json'))
|
||||
.toString()).version);
|
||||
}
|
||||
|
||||
commander
|
||||
|
||||
@@ -18,8 +18,16 @@ import {Group, Project} from '@openstapps/gitlab-api/lib/types';
|
||||
import {Logger} from '@openstapps/logger';
|
||||
import {readFile, writeFile} from 'fs';
|
||||
import {promisify} from 'util';
|
||||
import {CONCURRENCY} from './configuration';
|
||||
|
||||
/**
|
||||
* Promisified version of readFile
|
||||
*/
|
||||
export const readFilePromisified = promisify(readFile);
|
||||
|
||||
/**
|
||||
* Promisified version of writeFile
|
||||
*/
|
||||
export const writeFilePromisified = promisify(writeFile);
|
||||
|
||||
/**
|
||||
@@ -29,15 +37,15 @@ export const writeFilePromisified = promisify(writeFile);
|
||||
* @param groups List of groups
|
||||
*/
|
||||
export async function getProjects(api: Api, groups: number[]): Promise<Project[]> {
|
||||
Logger.info('Fetching all projects for specified groups (' + groups.length + ')...');
|
||||
Logger.info(`Fetching all projects for specified groups (${groups.length})...`);
|
||||
|
||||
const projectResults = await asyncPool(3, groups, (groupId) => {
|
||||
const projectResults = await asyncPool(CONCURRENCY, groups, async (groupId) => {
|
||||
return api.getProjectsForGroup(groupId);
|
||||
});
|
||||
|
||||
const projects = flatten2dArray(projectResults);
|
||||
|
||||
Logger.log('Fetched ' + projects.length + ' project(s).');
|
||||
Logger.log(`Fetched ${projects.length} project(s).`);
|
||||
|
||||
return projects;
|
||||
}
|
||||
@@ -49,8 +57,8 @@ export async function getProjects(api: Api, groups: number[]): Promise<Project[]
|
||||
* @param groups List of groups
|
||||
*/
|
||||
export async function getSubGroups(api: Api, groups: number[]): Promise<Group[]> {
|
||||
return flatten2dArray(await asyncPool(2, groups, async (groupId) => {
|
||||
return await api.getSubGroupsForGroup(groupId);
|
||||
return flatten2dArray(await asyncPool(CONCURRENCY, groups, async (groupId) => {
|
||||
return api.getSubGroupsForGroup(groupId);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,21 +17,27 @@ import {Label} from '@openstapps/gitlab-api/lib/types';
|
||||
/**
|
||||
* List of schools with their IDs
|
||||
*/
|
||||
export const SCHOOLS: { [school: string]: number } = {};
|
||||
export const SCHOOLS: { [school: string]: number; } = {};
|
||||
|
||||
/**
|
||||
* ID OF openstapps main group
|
||||
*/
|
||||
const STAPPS_GROUP_ID = 4088298;
|
||||
|
||||
/**
|
||||
* List of group IDs to fetch issues for
|
||||
*
|
||||
* 4088298 is openstapps main group
|
||||
*/
|
||||
export const GROUPS: number[] = [4088298].concat(Object.keys(SCHOOLS).map((school) => {
|
||||
export const GROUPS: number[] = [STAPPS_GROUP_ID]
|
||||
.concat(Object.keys(SCHOOLS)
|
||||
.map((school) => {
|
||||
return SCHOOLS[school];
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export const LABEL_WEIGHTS: any = {
|
||||
export const LABEL_WEIGHTS: { [key: string]: number; } = {
|
||||
'bug': 1,
|
||||
'critical': 2,
|
||||
};
|
||||
@@ -67,8 +73,7 @@ export const PROTECTED_BRANCHES = [
|
||||
/**
|
||||
* Labels to add to all projects
|
||||
*/
|
||||
export const NEEDED_LABELS: Label[] = [
|
||||
{
|
||||
export const NEEDED_LABELS: Label[] = [{
|
||||
color: '#FF0000',
|
||||
description: 'An error/something that is not working as expected',
|
||||
name: 'bug',
|
||||
@@ -154,18 +159,21 @@ export const NEEDED_LABELS: Label[] = [
|
||||
color: '#428BCA',
|
||||
description: 'Feedback from the feedback-module of the app',
|
||||
name: 'user-feedback',
|
||||
},
|
||||
].concat(Object.keys(SCHOOLS).map((school) => {
|
||||
}]
|
||||
.concat(Object.keys(SCHOOLS)
|
||||
.map((school) => {
|
||||
return {
|
||||
color: '#F0AD4E',
|
||||
description: 'An issue that specifically applies to this school',
|
||||
name: `school-${school}`,
|
||||
};
|
||||
})).concat(['android', 'iOS', 'web', 'node'].map((platform) => {
|
||||
}))
|
||||
.concat(['android', 'iOS', 'web', 'node']
|
||||
.map((platform) => {
|
||||
return {
|
||||
color: '#FFECDB',
|
||||
description: 'An issue that specifically applies to this platform',
|
||||
name: 'platform-' + platform,
|
||||
name: `platform-${platform}`,
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -178,3 +186,8 @@ export const NOTE_PREFIX = '`openstapps/projectmanagement`';
|
||||
* Slack channel to post messages to
|
||||
*/
|
||||
export const SLACK_CHANNEL = 'C762UG76Z';
|
||||
|
||||
/**
|
||||
* Concurrency for async pool
|
||||
*/
|
||||
export const CONCURRENCY = 3;
|
||||
|
||||
@@ -24,8 +24,13 @@ import {
|
||||
} from '@openstapps/gitlab-api/lib/types';
|
||||
import {Logger} from '@openstapps/logger';
|
||||
import {WebClient} from '@slack/client';
|
||||
import {GROUPS, NOTE_PREFIX, SLACK_CHANNEL} from '../configuration';
|
||||
import {CONCURRENCY, GROUPS, NOTE_PREFIX, SLACK_CHANNEL} from '../configuration';
|
||||
|
||||
/**
|
||||
* Remind people of open merge requests
|
||||
*
|
||||
* @param api GitLab API to make requests with
|
||||
*/
|
||||
export async function remind(api: Api): Promise<void> {
|
||||
// get list of open merge requests
|
||||
const mergeRequests = await api.getMergeRequests(MembershipScope.GROUPS, GROUPS[0], MergeRequestState.OPENED);
|
||||
@@ -51,10 +56,11 @@ export async function remind(api: Api): Promise<void> {
|
||||
|
||||
Logger.info(`Found ${maintainers.length} maintainer(s).`);
|
||||
|
||||
await asyncPool(2, mergeRequests, async (mergeRequest) => {
|
||||
await asyncPool(CONCURRENCY, mergeRequests, async (mergeRequest) => {
|
||||
// check if merge request is WIP
|
||||
if (mergeRequest.work_in_progress) {
|
||||
Logger.info(`Merge request '${mergeRequest.title}' is WIP.`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -67,14 +73,14 @@ export async function remind(api: Api): Promise<void> {
|
||||
// check if at least one of the discussions is unresolved
|
||||
const hasUnresolvedDiscussions = discussions.some((discussion) => {
|
||||
return discussion.notes.some((note) => {
|
||||
return note.resolvable && !note.resolved;
|
||||
return note.resolvable && (typeof note.resolved === 'undefined' || !note.resolved);
|
||||
});
|
||||
});
|
||||
|
||||
if (hasUnresolvedDiscussions) {
|
||||
let recipient = mergeRequest.author.username;
|
||||
|
||||
if (typeof mergeRequest.assignee !== 'undefined') {
|
||||
if (typeof mergeRequest.assignee !== 'undefined' && mergeRequest.assignee !== null) {
|
||||
recipient = mergeRequest.assignee.username;
|
||||
}
|
||||
|
||||
@@ -100,11 +106,16 @@ export async function remind(api: Api): Promise<void> {
|
||||
return true;
|
||||
}
|
||||
|
||||
return approval.approved_by.find((approver: { user: User }) => {
|
||||
return approval.approved_by.find((approver: {
|
||||
/**
|
||||
* Possible approver
|
||||
*/
|
||||
user: User;
|
||||
}) => {
|
||||
return approver.user.username !== username;
|
||||
});
|
||||
})
|
||||
.map((username) => '@' + username)
|
||||
.map((username) => `@${username}`)
|
||||
.join(', ');
|
||||
|
||||
// send message to slack
|
||||
@@ -131,7 +142,7 @@ export async function remind(api: Api): Promise<void> {
|
||||
|
||||
// prefix maintainers with '@' and join with commas
|
||||
const possibleMergers = maintainerUsernames
|
||||
.map((username) => '@' + username)
|
||||
.map((username) => `@${username}`)
|
||||
.join(', ');
|
||||
|
||||
// create note in merge request
|
||||
|
||||
@@ -21,14 +21,23 @@ import {render} from 'mustache';
|
||||
import {join, resolve} from 'path';
|
||||
import {cwd} from 'process';
|
||||
import {flatten2dArray, getProjects, readFilePromisified, writeFilePromisified} from '../common';
|
||||
import {BOLD_LABELS, GROUPS, LABEL_WEIGHTS} from '../configuration';
|
||||
import {BOLD_LABELS, CONCURRENCY, GROUPS, LABEL_WEIGHTS} from '../configuration';
|
||||
|
||||
/**
|
||||
* A structure for template compilation
|
||||
*/
|
||||
export interface StructureForTemplate {
|
||||
/**
|
||||
* List of issues by assignee
|
||||
*/
|
||||
issuesByAssignee: AssigneeWithIssues[];
|
||||
/**
|
||||
* Meeting day
|
||||
*/
|
||||
meetingDay: string;
|
||||
/**
|
||||
* Timestamp, when the report was generated
|
||||
*/
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
@@ -43,12 +52,30 @@ export interface IssuesGroupedByAssigneeId {
|
||||
* An assignee with assigned issues
|
||||
*/
|
||||
export interface AssigneeWithIssues {
|
||||
/**
|
||||
* Assignee of the issues
|
||||
*/
|
||||
assignee: User;
|
||||
/**
|
||||
* Counts of issues
|
||||
*/
|
||||
issueCounts: {
|
||||
/**
|
||||
* Count of closed issues
|
||||
*/
|
||||
closed: number;
|
||||
/**
|
||||
* Count of opened issues
|
||||
*/
|
||||
opened: number;
|
||||
};
|
||||
/**
|
||||
* List of issues
|
||||
*/
|
||||
issues: IssueWithMeta[];
|
||||
/**
|
||||
* Quota of closed issues
|
||||
*/
|
||||
quota: number;
|
||||
}
|
||||
|
||||
@@ -56,14 +83,38 @@ export interface AssigneeWithIssues {
|
||||
* Issue with meta information
|
||||
*/
|
||||
export interface IssueWithMeta extends Issue {
|
||||
/**
|
||||
* Whether or not an issue branch exists
|
||||
*/
|
||||
$branchExists: boolean;
|
||||
/**
|
||||
* Whether or not the issue is closed
|
||||
*/
|
||||
$closed: boolean;
|
||||
/**
|
||||
* List of labels
|
||||
*/
|
||||
$labels: Array<{
|
||||
/**
|
||||
* Whether or not to print the label bold
|
||||
*/
|
||||
bold: boolean;
|
||||
/**
|
||||
* Actual label
|
||||
*/
|
||||
label: string;
|
||||
}>;
|
||||
/**
|
||||
* URL of merge request
|
||||
*/
|
||||
$mergeRequestUrl: string;
|
||||
/**
|
||||
* Name of project
|
||||
*/
|
||||
$project: string;
|
||||
/**
|
||||
* Number of weeks the issue is open
|
||||
*/
|
||||
$weeksOpen: number;
|
||||
}
|
||||
|
||||
@@ -72,7 +123,13 @@ export interface IssueWithMeta extends Issue {
|
||||
*/
|
||||
export interface MergeRequestsForProjects {
|
||||
[projectId: string]: Array<{
|
||||
/**
|
||||
* IID of issue
|
||||
*/
|
||||
issue_iid: number;
|
||||
/**
|
||||
* URL of issue
|
||||
*/
|
||||
web_url: string;
|
||||
}>;
|
||||
}
|
||||
@@ -100,9 +157,11 @@ export function getMergeRequestUrls(projectMergeRequests: MergeRequestsForProjec
|
||||
return [];
|
||||
}
|
||||
|
||||
return projectMergeRequests[projectId].filter((obj) => {
|
||||
return projectMergeRequests[projectId]
|
||||
.filter((obj) => {
|
||||
return obj.issue_iid === issueIid;
|
||||
}).map((obj) => {
|
||||
})
|
||||
.map((obj) => {
|
||||
return obj.web_url;
|
||||
});
|
||||
}
|
||||
@@ -115,17 +174,18 @@ export function getMergeRequestUrls(projectMergeRequests: MergeRequestsForProjec
|
||||
* @param groups List of groups to get issues for
|
||||
*/
|
||||
export async function getIssues(api: Api, label: string, groups: number[]): Promise<Issue[]> {
|
||||
const issueResults = await asyncPool(2, groups, (groupId) => {
|
||||
const issueResults = await asyncPool(CONCURRENCY, groups, async (groupId) => {
|
||||
return api.getIssues({
|
||||
groupId: groupId,
|
||||
});
|
||||
});
|
||||
|
||||
const issues = flatten2dArray(issueResults).filter((issue) => {
|
||||
const issues = flatten2dArray(issueResults)
|
||||
.filter((issue) => {
|
||||
return issue.labels.indexOf(label) >= 0;
|
||||
});
|
||||
|
||||
Logger.log('Fetched ' + issues.length + ' issue(s).');
|
||||
Logger.log(`Fetched ${issues.length} issue(s).`);
|
||||
|
||||
return issues;
|
||||
}
|
||||
@@ -138,18 +198,21 @@ export async function getIssues(api: Api, label: string, groups: number[]): Prom
|
||||
*/
|
||||
export async function getIssueBranches(
|
||||
api: Api,
|
||||
projects: Project[]): Promise<{ [k: string]: number[] }> {
|
||||
const projectBranches: { [k: string]: number[] } = {};
|
||||
projects: Project[]): Promise<{ [k: string]: number[]; }> {
|
||||
const projectBranches: { [k: string]: number[]; } = {};
|
||||
|
||||
await asyncPool(2, projects, async (project) => {
|
||||
await asyncPool(CONCURRENCY, projects, async (project) => {
|
||||
const branches = await api.getBranchesForProject(project.id);
|
||||
|
||||
// extract issue number from branch
|
||||
projectBranches[project.id] = branches.map((branch) => {
|
||||
projectBranches[project.id] = branches
|
||||
.map((branch) => {
|
||||
return branch.name.split('-')[0];
|
||||
}).filter((branchNameStart) => {
|
||||
})
|
||||
.filter((branchNameStart) => {
|
||||
return branchNameStart.match(/^[0-9]+$/);
|
||||
}).map((branchNameStart) => {
|
||||
})
|
||||
.map((branchNameStart) => {
|
||||
return parseInt(branchNameStart, 10);
|
||||
});
|
||||
});
|
||||
@@ -166,7 +229,7 @@ export async function getIssueBranches(
|
||||
export async function getIssuesGroupedByAssignees(api: Api, label: string): Promise<AssigneeWithIssues[]> {
|
||||
const issuesByAssignee: IssuesGroupedByAssigneeId = {};
|
||||
|
||||
const groups = flatten2dArray(await asyncPool(2, GROUPS, async (groupId) => {
|
||||
const groups = flatten2dArray(await asyncPool(CONCURRENCY, GROUPS, async (groupId) => {
|
||||
return (await api.getSubGroupsForGroup(groupId)).map((group) => {
|
||||
return group.id;
|
||||
});
|
||||
@@ -184,6 +247,7 @@ export async function getIssuesGroupedByAssignees(api: Api, label: string): Prom
|
||||
issues.forEach((issue) => {
|
||||
if (issue.assignee === null) {
|
||||
Logger.warn('Issue without assignee!', issue.web_url);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -217,8 +281,11 @@ export async function getIssuesGroupedByAssignees(api: Api, label: string): Prom
|
||||
};
|
||||
}),
|
||||
$mergeRequestUrl: getMergeRequestUrls(mergeRequests, issue.project_id, issue.iid)[0],
|
||||
$project: issue.web_url.replace('https://gitlab.com/', '').split('/issues/')[0],
|
||||
$weeksOpen: moment().diff(moment(issue.created_at), 'weeks'),
|
||||
$project: issue.web_url
|
||||
.replace('https://gitlab.com/', '')
|
||||
.split('/issues/')[0],
|
||||
$weeksOpen: moment()
|
||||
.diff(moment(issue.created_at), 'weeks'),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -229,25 +296,38 @@ export async function getIssuesGroupedByAssignees(api: Api, label: string): Prom
|
||||
});
|
||||
|
||||
// calculate quota
|
||||
Object.keys(issuesByAssignee).forEach((_assigneeId) => {
|
||||
for (const _assigneeId in issuesByAssignee) {
|
||||
if (!issuesByAssignee.hasOwnProperty(_assigneeId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const assigneeId = parseInt(_assigneeId, 10);
|
||||
|
||||
issuesByAssignee[assigneeId].quota = Math.floor(
|
||||
issuesByAssignee[assigneeId].issueCounts.closed
|
||||
/ (issuesByAssignee[assigneeId].issueCounts.opened
|
||||
// tslint:disable-next-line:no-magic-numbers
|
||||
+ issuesByAssignee[assigneeId].issueCounts.closed) * 100,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// sort issues by weight of labels and status
|
||||
Object.keys(issuesByAssignee).forEach((_assigneeId) => {
|
||||
for (const _assigneeId in issuesByAssignee) {
|
||||
if (!issuesByAssignee.hasOwnProperty(_assigneeId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const assigneeId = parseInt(_assigneeId, 10);
|
||||
|
||||
issuesByAssignee[assigneeId].issues.sort((a, b) => {
|
||||
let weightA = 0;
|
||||
let weightB = 0;
|
||||
|
||||
Object.keys(LABEL_WEIGHTS).forEach((issueLabel) => {
|
||||
for (const issueLabel in LABEL_WEIGHTS) {
|
||||
if (!LABEL_WEIGHTS.hasOwnProperty(issueLabel)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a.labels.indexOf(issueLabel) >= 0) {
|
||||
weightA += LABEL_WEIGHTS[issueLabel];
|
||||
}
|
||||
@@ -255,19 +335,21 @@ export async function getIssuesGroupedByAssignees(api: Api, label: string): Prom
|
||||
if (b.labels.indexOf(issueLabel) >= 0) {
|
||||
weightB += LABEL_WEIGHTS[issueLabel];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (a.state === IssueState.CLOSED) {
|
||||
// tslint:disable-next-line:no-magic-numbers
|
||||
weightA -= 10;
|
||||
}
|
||||
|
||||
if (b.state === IssueState.CLOSED) {
|
||||
// tslint:disable-next-line:no-magic-numbers
|
||||
weightB -= 10;
|
||||
}
|
||||
|
||||
return weightB - weightA;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Object.values(issuesByAssignee);
|
||||
}
|
||||
@@ -280,7 +362,12 @@ export function getNextMeetingDay() {
|
||||
const now = moment();
|
||||
|
||||
// get first wednesday of month
|
||||
const meetingDayMoment = moment().startOf('month').hour(10).isoWeekday(3);
|
||||
const meetingDayMoment = moment()
|
||||
.startOf('month')
|
||||
// tslint:disable-next-line:no-magic-numbers
|
||||
.hour(10)
|
||||
// tslint:disable-next-line:no-magic-numbers
|
||||
.isoWeekday(3);
|
||||
|
||||
while (meetingDayMoment.isBefore(now)) {
|
||||
// add one week until meeting day is after now
|
||||
@@ -290,7 +377,7 @@ export function getNextMeetingDay() {
|
||||
const meetingDay = meetingDayMoment.format('YYYY-MM-DD');
|
||||
|
||||
// log found meeting day
|
||||
Logger.info('Generating report for ' + meetingDay + ' of ' + GROUPS.length + ' group(s)...');
|
||||
Logger.info(`Generating report for '${meetingDay}' of '${GROUPS.length}' group(s)...`);
|
||||
|
||||
return meetingDay;
|
||||
}
|
||||
@@ -306,7 +393,7 @@ export async function getMergeRequests(api: Api,
|
||||
const projectMergeRequests: MergeRequestsForProjects = {};
|
||||
|
||||
// iterate over projects
|
||||
await asyncPool(2, projects, async (project) => {
|
||||
await asyncPool(CONCURRENCY, projects, async (project) => {
|
||||
// check if project can have merge requests
|
||||
if (!project.merge_requests_enabled) {
|
||||
return;
|
||||
@@ -316,12 +403,15 @@ export async function getMergeRequests(api: Api,
|
||||
const mergeRequests = await api.getMergeRequests(MembershipScope.PROJECTS, project.id, MergeRequestState.OPENED);
|
||||
|
||||
// extract issue number from merge request
|
||||
projectMergeRequests[project.id] = mergeRequests.map((mergeRequest) => {
|
||||
projectMergeRequests[project.id] = mergeRequests
|
||||
.map((mergeRequest) => {
|
||||
// keep information about web url too
|
||||
return {issue_iid: mergeRequest.source_branch.split('-')[0], web_url: mergeRequest.web_url};
|
||||
}).filter((branchNameStartAndUrl) => {
|
||||
})
|
||||
.filter((branchNameStartAndUrl) => {
|
||||
return branchNameStartAndUrl.issue_iid.match(/^[0-9]+$/);
|
||||
}).map((branchNameStartAndUrl) => {
|
||||
})
|
||||
.map((branchNameStartAndUrl) => {
|
||||
return {
|
||||
issue_iid: parseInt(branchNameStartAndUrl.issue_iid, 10),
|
||||
web_url: branchNameStartAndUrl.web_url,
|
||||
@@ -345,7 +435,8 @@ export async function generateReport(api: Api, label: string, template: string):
|
||||
const structureForTemplate: StructureForTemplate = {
|
||||
issuesByAssignee: issuesGroupedByAssignee,
|
||||
meetingDay: getNextMeetingDay(),
|
||||
timestamp: moment().format('LLL'),
|
||||
timestamp: moment()
|
||||
.format('LLL'),
|
||||
};
|
||||
|
||||
return render(
|
||||
@@ -369,7 +460,7 @@ export async function report(api: Api, label: string) {
|
||||
(await readFilePromisified(resolve(__dirname, '..', '..', 'templates', 'report.md.mustache'))).toString(),
|
||||
);
|
||||
|
||||
let filename = join(cwd(), 'reports', meetingDay + '.md');
|
||||
let filename = join(cwd(), 'reports', `${meetingDay}.md`);
|
||||
|
||||
if (label !== 'meeting') {
|
||||
filename = join(cwd(), 'reports', `${label}.md`);
|
||||
|
||||
@@ -17,7 +17,15 @@ import {Api} from '@openstapps/gitlab-api';
|
||||
import {AccessLevel, IssueState, MembershipScope, Milestone, Project, Scope} from '@openstapps/gitlab-api/lib/types';
|
||||
import {Logger} from '@openstapps/logger';
|
||||
import {flatten2dArray, getProjects} from '../common';
|
||||
import {GROUPS, NEEDED_LABELS, NEEDED_MILESTONES, NOTE_PREFIX, PROTECTED_BRANCHES, SCHOOLS} from '../configuration';
|
||||
import {
|
||||
CONCURRENCY,
|
||||
GROUPS,
|
||||
NEEDED_LABELS,
|
||||
NEEDED_MILESTONES,
|
||||
NOTE_PREFIX,
|
||||
PROTECTED_BRANCHES,
|
||||
SCHOOLS,
|
||||
} from '../configuration';
|
||||
|
||||
/**
|
||||
* Tidy issues without milestone
|
||||
@@ -28,7 +36,7 @@ import {GROUPS, NEEDED_LABELS, NEEDED_MILESTONES, NOTE_PREFIX, PROTECTED_BRANCHE
|
||||
*/
|
||||
export async function tidyIssuesWithoutMilestone(api: Api): Promise<void> {
|
||||
// fetch issues without milestone from all groups
|
||||
const issueResults = await asyncPool(3, GROUPS, (groupId) => {
|
||||
const issueResults = await asyncPool(CONCURRENCY, GROUPS, async (groupId) => {
|
||||
return api.getIssues({
|
||||
groupId: groupId,
|
||||
milestone: 'No Milestone',
|
||||
@@ -39,11 +47,11 @@ export async function tidyIssuesWithoutMilestone(api: Api): Promise<void> {
|
||||
// flatten structure, e.g. put all issues in one array
|
||||
const issuesWithoutMilestone = flatten2dArray(issueResults);
|
||||
|
||||
Logger.info('Found `' + issuesWithoutMilestone.length + '` issue(s) without milestone.');
|
||||
Logger.info(`Found '${issuesWithoutMilestone.length}' issue(s) without milestone.`);
|
||||
|
||||
const milestoneCache: { [s: number]: Milestone[] } = {};
|
||||
const milestoneCache: { [s: number]: Milestone[]; } = {};
|
||||
|
||||
await asyncPool(5, issuesWithoutMilestone, async (issue) => {
|
||||
await asyncPool(CONCURRENCY, issuesWithoutMilestone, async (issue) => {
|
||||
if (typeof milestoneCache[issue.project_id] === 'undefined') {
|
||||
milestoneCache[issue.project_id] = await api.getMilestonesForProject(issue.project_id);
|
||||
}
|
||||
@@ -57,19 +65,20 @@ export async function tidyIssuesWithoutMilestone(api: Api): Promise<void> {
|
||||
});
|
||||
|
||||
if (milestoneId === null) {
|
||||
Logger.warn('Milestone `Meeting` was not available for issue ' + issue.title + ' (' + issue.web_url + ').');
|
||||
Logger.warn(`Milestone 'Meeting' was not available for issue ${issue.title} (${issue.web_url}).`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await api.setMilestoneForIssue(issue, milestoneId);
|
||||
|
||||
Logger.log('Milestone was set to `Meeting` for issue ' + issue.title + ' (' + issue.web_url + ').');
|
||||
Logger.log(`Milestone was set to 'Meeting' for issue ${issue.title} (${issue.web_url})`);
|
||||
|
||||
await api.createNote(
|
||||
issue.project_id,
|
||||
Scope.ISSUES,
|
||||
issue.iid,
|
||||
`${NOTE_PREFIX} Milestone was set automatically to \`Meeting\`.`,
|
||||
`${NOTE_PREFIX} Milestone was set automatically to 'Meeting'.`,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -85,7 +94,7 @@ export async function tidyIssuesWithoutMilestone(api: Api): Promise<void> {
|
||||
*/
|
||||
export async function tidyOpenIssuesWithoutMeetingLabel(api: Api): Promise<void> {
|
||||
// fetch all open issues
|
||||
const issueResults = await asyncPool(3, GROUPS, (groupId) => {
|
||||
const issueResults = await asyncPool(CONCURRENCY, GROUPS, async (groupId) => {
|
||||
return api.getIssues({
|
||||
groupId: groupId,
|
||||
state: IssueState.OPENED,
|
||||
@@ -104,9 +113,10 @@ export async function tidyOpenIssuesWithoutMeetingLabel(api: Api): Promise<void>
|
||||
|
||||
Logger.info(`Filtered ${openIssuesWithoutMeetingLabel.length} open issue(s) without label 'meeting'.`);
|
||||
|
||||
await asyncPool(5, openIssuesWithoutMeetingLabel, async (issue) => {
|
||||
await asyncPool(CONCURRENCY, openIssuesWithoutMeetingLabel, async (issue) => {
|
||||
if (issue.milestone !== null && issue.milestone.title === 'Backlog') {
|
||||
Logger.info(`Skipping issue "${issue.title}" because it is in backlog.`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -127,7 +137,7 @@ export async function tidyOpenIssuesWithoutMeetingLabel(api: Api): Promise<void>
|
||||
* @param projects List of projects to tidy labels on
|
||||
*/
|
||||
export async function tidyLabels(api: Api, projects: Project[]): Promise<void> {
|
||||
await asyncPool(5, projects, async (project) => {
|
||||
await asyncPool(CONCURRENCY, projects, async (project) => {
|
||||
const labels = await api.getLabels(project.id);
|
||||
|
||||
const neededLabels = NEEDED_LABELS.slice(0);
|
||||
@@ -148,10 +158,10 @@ export async function tidyLabels(api: Api, projects: Project[]): Promise<void> {
|
||||
} */
|
||||
});
|
||||
|
||||
await asyncPool(2, neededLabels, async (neededLabel) => {
|
||||
await asyncPool(CONCURRENCY, neededLabels, async (neededLabel) => {
|
||||
await api.createLabel(project.id, neededLabel.name, neededLabel.description, neededLabel.color);
|
||||
|
||||
Logger.log('Created label `' + neededLabel.name + '` in ' + project.name_with_namespace + '.');
|
||||
Logger.log(`Created label '${neededLabel.name}' in '${project.name_with_namespace}'.`);
|
||||
});
|
||||
|
||||
// await asyncPool(2, extraneousLabels, async (extraneousLabel) => {
|
||||
@@ -171,7 +181,7 @@ export async function tidyLabels(api: Api, projects: Project[]): Promise<void> {
|
||||
* @param projects List of projects to tidy milestones on
|
||||
*/
|
||||
export async function tidyMilestones(api: Api, projects: Project[]): Promise<void> {
|
||||
await asyncPool(5, projects, async (project) => {
|
||||
await asyncPool(CONCURRENCY, projects, async (project) => {
|
||||
const milestones = await api.getMilestonesForProject(project.id);
|
||||
const missingMilestones = NEEDED_MILESTONES.slice(0);
|
||||
|
||||
@@ -184,9 +194,9 @@ export async function tidyMilestones(api: Api, projects: Project[]): Promise<voi
|
||||
});
|
||||
|
||||
if (missingMilestones.length > 0) {
|
||||
await asyncPool(2, missingMilestones, async (milestone) => {
|
||||
await asyncPool(CONCURRENCY, missingMilestones, async (milestone) => {
|
||||
await api.createMilestone(project.id, milestone);
|
||||
Logger.log('Created milestone ' + milestone + ' for project ' + project.name_with_namespace + '.');
|
||||
Logger.log(`Created milestone '${milestone}' for project ${project.name_with_namespace}'.`);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -201,7 +211,7 @@ export async function tidyMilestones(api: Api, projects: Project[]): Promise<voi
|
||||
* @param projects List of projects to tidy milestones on
|
||||
*/
|
||||
export async function tidyProtectedBranches(api: Api, projects: Project[]): Promise<void> {
|
||||
await asyncPool(2, projects, async (project) => {
|
||||
await asyncPool(CONCURRENCY, projects, async (project) => {
|
||||
const branches = await api.getBranchesForProject(project.id);
|
||||
|
||||
const protectableBranches = branches.filter((branch) => {
|
||||
@@ -212,10 +222,10 @@ export async function tidyProtectedBranches(api: Api, projects: Project[]): Prom
|
||||
return !branch.protected;
|
||||
});
|
||||
|
||||
await asyncPool(2, unprotectedBranches, async (branch) => {
|
||||
await asyncPool(CONCURRENCY, unprotectedBranches, async (branch) => {
|
||||
await api.protectBranch(project.id, branch.name);
|
||||
|
||||
Logger.log('Added protected branch `' + branch.name + '` in project `' + project.name_with_namespace + '`...');
|
||||
Logger.log(`Added protected branch '${branch.name}' in project '${project.name_with_namespace}'...`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -229,18 +239,31 @@ export async function tidyProtectedBranches(api: Api, projects: Project[]): Prom
|
||||
* @param projects List of projects to tidy protected tags on
|
||||
*/
|
||||
export async function tidyProtectedTags(api: Api, projects: Project[]): Promise<void> {
|
||||
await asyncPool(2, projects, async (project) => {
|
||||
await asyncPool(CONCURRENCY, projects, async (project) => {
|
||||
// TODO: move this to GitLab API
|
||||
const protectedTags: Array<{
|
||||
/**
|
||||
* List of access levels to create a tag
|
||||
*/
|
||||
create_access_levels: Array<{
|
||||
access_level: AccessLevel,
|
||||
/**
|
||||
* Access level
|
||||
*/
|
||||
access_level: AccessLevel;
|
||||
/**
|
||||
* Description of access level
|
||||
*/
|
||||
access_level_description: string;
|
||||
}>;
|
||||
/**
|
||||
* Name of the tag
|
||||
*/
|
||||
name: string;
|
||||
}> = await api.makeGitLabAPIRequest(`projects/${project.id}/protected_tags`);
|
||||
|
||||
if (!protectedTags.find((protectedTag) => {
|
||||
if (protectedTags.findIndex((protectedTag) => {
|
||||
return protectedTag.name === 'v*';
|
||||
})) {
|
||||
}) >= 0) {
|
||||
await api.makeGitLabAPIRequest(`projects/${project.id}/protected_tags`, {
|
||||
data: {
|
||||
create_access_level: AccessLevel.Maintainer,
|
||||
@@ -265,34 +288,35 @@ export async function tidySubGroupMembers(api: Api): Promise<void> {
|
||||
const stappsMembers = await api.getMembers(MembershipScope.GROUPS, GROUPS[0]);
|
||||
const stappsMemberIds = stappsMembers.map((member) => member.id);
|
||||
|
||||
const groupIdsToSchool: any = {};
|
||||
const groupIdsToSchool: { [id: number]: string; } = {};
|
||||
|
||||
Object.keys(SCHOOLS).map((school) => {
|
||||
Object.keys(SCHOOLS)
|
||||
.map((school) => {
|
||||
groupIdsToSchool[SCHOOLS[school]] = school;
|
||||
});
|
||||
|
||||
await asyncPool(2, GROUPS.slice(1), async (groupId) => {
|
||||
await asyncPool(CONCURRENCY, GROUPS.slice(1), async (groupId) => {
|
||||
const members = await api.getMembers(MembershipScope.GROUPS, groupId);
|
||||
const memberIds = members.map((member) => member.id);
|
||||
|
||||
await asyncPool(2, stappsMembers, async (stappsMember) => {
|
||||
await asyncPool(CONCURRENCY, stappsMembers, async (stappsMember) => {
|
||||
if (memberIds.indexOf(stappsMember.id) === -1) {
|
||||
await api.addMember(MembershipScope.GROUPS, groupId, stappsMember.id, AccessLevel.Developer);
|
||||
|
||||
Logger.log('Added ' + stappsMember.name + ' to group ' + groupIdsToSchool[groupId] + '.');
|
||||
Logger.log(`Added '${stappsMember.name}' to group '${groupIdsToSchool[groupId]}'.`);
|
||||
}
|
||||
});
|
||||
|
||||
await asyncPool(2, members, async (member) => {
|
||||
await asyncPool(CONCURRENCY, members, async (member) => {
|
||||
if (stappsMemberIds.indexOf(member.id) === -1) {
|
||||
await api.deleteMember(MembershipScope.GROUPS, groupId, member.id);
|
||||
|
||||
Logger.log('Deleted member ' + member.name + ' from group ' + groupIdsToSchool[groupId] + '.');
|
||||
Logger.log(`Deleted member '${member.name}' from group '${groupIdsToSchool[groupId]}'.`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Logger.ok('Tidied "sub" group members.');
|
||||
Logger.ok(`Tidied 'sub' group members.`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,11 +324,11 @@ export async function tidySubGroupMembers(api: Api): Promise<void> {
|
||||
*
|
||||
* Set assignee to author if no assignee is set.
|
||||
*
|
||||
* @param api
|
||||
* @param api GitLab API instance to use for the requests
|
||||
*/
|
||||
export async function tidyIssuesWithoutAssignee(api: Api): Promise<void> {
|
||||
// fetch issues without milestone from all groups
|
||||
const issueResults = await asyncPool(3, GROUPS, (groupId) => {
|
||||
const issueResults = await asyncPool(CONCURRENCY, GROUPS, async (groupId) => {
|
||||
return api.getIssues({
|
||||
groupId: groupId,
|
||||
state: IssueState.OPENED,
|
||||
@@ -318,12 +342,12 @@ export async function tidyIssuesWithoutAssignee(api: Api): Promise<void> {
|
||||
return issue.assignee === null;
|
||||
});
|
||||
|
||||
Logger.info('Found `' + issuesWithoutAssignee.length + '` issue(s) without assignee.');
|
||||
Logger.info(`Found '${issuesWithoutAssignee.length}' issue(s) without assignee.`);
|
||||
|
||||
await asyncPool(3, issuesWithoutAssignee, async (issue) => {
|
||||
await asyncPool(CONCURRENCY, issuesWithoutAssignee, async (issue) => {
|
||||
await api.setAssigneeForIssue(issue, issue.author.id);
|
||||
|
||||
Logger.log('Set assignee for `' + issue.title + '` to ' + issue.author.name + '.');
|
||||
Logger.log(`Set assignee for '${issue.title}' to '${issue.author.name}'.`);
|
||||
|
||||
await api.createNote(
|
||||
issue.project_id,
|
||||
|
||||
@@ -17,7 +17,7 @@ import {Api} from '@openstapps/gitlab-api';
|
||||
import {IssueState, Scope} from '@openstapps/gitlab-api/lib/types';
|
||||
import {Logger} from '@openstapps/logger';
|
||||
import {flatten2dArray} from '../common';
|
||||
import {GROUPS, NOTE_PREFIX} from '../configuration';
|
||||
import {CONCURRENCY, GROUPS, NOTE_PREFIX} from '../configuration';
|
||||
|
||||
/**
|
||||
* Remove label `meeting` from closed issues
|
||||
@@ -25,7 +25,7 @@ import {GROUPS, NOTE_PREFIX} from '../configuration';
|
||||
* @param api Instance of GitLabAPI to send requests with
|
||||
*/
|
||||
export async function unlabel(api: Api) {
|
||||
const issueResults = await asyncPool(3, GROUPS, (groupId) => {
|
||||
const issueResults = await asyncPool(CONCURRENCY, GROUPS, async (groupId) => {
|
||||
return api.getIssues({
|
||||
groupId: groupId,
|
||||
state: IssueState.CLOSED,
|
||||
@@ -34,9 +34,9 @@ export async function unlabel(api: Api) {
|
||||
|
||||
const issues = flatten2dArray(issueResults);
|
||||
|
||||
Logger.log('Fetched ' + issues.length + ' closed issue(s).');
|
||||
Logger.log(`Fetched ${issues.length} closed issue(s).`);
|
||||
|
||||
await asyncPool(1, issues, async (issue) => {
|
||||
await asyncPool(CONCURRENCY, issues, async (issue) => {
|
||||
if (issue.labels.indexOf('meeting') >= 0) {
|
||||
Logger.info(`Issue ${issue.title} is closed and has label "meeting". Removing it.`);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user