Todo a la vez: comprobación automática del tamaño del paquete

Hola a todos, mi nombre es Ilya. He estado trabajando en TI durante aproximadamente 6 años, los últimos 2 años, en la compañía Yandex.Money como desarrollador front-end. Las responsabilidades incluyen el mantenimiento / desarrollo de partes de las aplicaciones, en este momento el proyecto "Mi cuenta" (y no, no es solo "corregir la sangría incorrecta", "cambiar el color del botón" o "cambiar rápidamente la forma").


En el departamento de desarrollo, tenemos un proyecto de Desafíos internos: llamamos tareas que no están directamente relacionadas con los productos principales. Estas son tareas que serán socialmente útiles y ayudarán a mejorar la infraestructura, CI / CD y más. Por ejemplo, para refinar el bot que notifica sobre lanzamientos, cree un complemento para IDEA para la autogeneración de afirmación por objeto y refactorice la antigua biblioteca de UI. Tales "desafíos" se pueden tomar a voluntad: son excelentes para diluir las tareas comerciales.


En un momento, me di cuenta hace mucho tiempo de que estaba interesado en la tarea de implementar la verificación automática del tamaño de la aplicación del paquete del cliente. Después de todo, todo lo que se puede verificar automáticamente es mejor verificarlo automáticamente.


Naturalmente, el resultado no fue solo una tarea implementada específica, sino una biblioteca con reglas y funciones generales (núcleo / núcleo) para implementar cualquier corrector (espero). En el proceso de resolución del problema, los conos estaban llenos de los cuales también quiero hablar.


Entonces, hay Jenkins, Bitbucket, correo (o Telegram, Slack, lo que necesita) y la necesidad de verificar el tamaño del paquete. Necesitamos hacer todos estos amigos.


imagen


Hay 2 verificaciones automáticas en Bitbucket para cada solicitud de extracción, sin las cuales la fusión es imposible: la tarea en Jira se transfiere al estado "Verificado", y todas las verificaciones automáticas en Jenkins se inician y se completan con éxito. Además, de acuerdo con el enlace configurado en Bitbucket, Jenkins recibe notificaciones sobre un cambio de rama para el cual se lanza el paquete completo de cheques. El trabajo con cheque (commit_checker) tiene muchas opciones:


  • lanzar script de pelusa;
  • ejecutar pruebas;
  • — , pull request , merge build publish Nexus, npm;
  • package.json.

, Bitbucket commit_checker. , , . challenge- commit_checker pipeline .
checker- -?


( c)


. , ( ) — pull request. , , . , : , , , , 3 . , ( ). , , merge .


. , , : 600 (, , , - ).


— , , pull request , merge , . .


, ...


, . , , API ( , Telegram). , .


. Jenkins job ( job). groove- , npm- , groove bin . , — , Bitbucket , merge .


, , : groove- node — .



: Groove Node?


Groove— , - .1. : , JS, // JS, Groove.
2. .
Node1. : , Node.
2. , /.
3. /Jira API/Bitbucket API.
4. -, , [size-limit](https://www.npmjs.com/package/size-limit).
1. Node, Jenkins .
2. API Jenkins /.

, Node.


«, », 3


, . ( , Jira), . , .




, checker-core. , , .


. — : -, /, dist, , . — .


— pull request.


— ? :


  • dev/master ( ), - . — .
  • / . — , .
  • Git , , package.json, API Bitbucket dev/master , . , .

— ?


, , , — . — . , , .


, , BitBucket ( Jenkins package.json). …


, , : «!», , , , .


?



. - , : , - .


. : , , … : « Jenkins? - », — , Jenkins ( ). -> -> -> .


, . , , (, ). , .



(, ) .


size-limit: , , .


. , … !


, roadmap .



, , size-limit , — , . , , API , CLI. . cli- . .


, -, , , .


:


Checker-core


!


/src —   
    |__ /tasks —      ; ,  ,  ,   ,  .    - `job`.
    |__ /models — ,   ;      `core`. ,      , , npm-.    `job`,  `tasks`   ,   `job,`   .  ,          `tasks`   `job`.
    |__ /types —  .
    |__ /utils —   (, ,    (npm)).
    |__ /errors — ,   .
    |__ /enums —  .
    |__ /decorators — ,   `utils`, `tasks`, `job`.
    |__ /declarations —  ,     Typescript.


Checker


Checker , . , , ..


Module


Module /, . ( , ..).



get-issue-assignees


. Checker project, assignee, lead.
Examples


import {
    Checker,
    Logger,
    IChecker,
    JiraManagerSettings,
    GetIssueAssignee
} from 'checker-core';
import {JiraSettings} from 'jira-manager';

import {IJobSettings} from '../types/IJobSettings';

export class ExampleJob {
    readonly _checker: IChecker;
    _jiraSettings: JiraManagerSettings;

    constructor(settings: IJobSettings) {
        if (settings === void 0) {
            throw new Error('Has no Checker settings');
        }

        this._checker = new Checker(settings.checker);
        this._jiraSettings = new JiraSettings(settings.jira);

        Logger.info('-- ExampleJob:');
        Logger.info(this._checker.appName);
    }

    start() {
        const getIssueAssignee = new GetIssueAssignee();

        return getIssueAssignee.run(this._checker, this._jiraSettings);
    }
}

get-last-merged-issue


. Checker .
Examples


import {Checker, Logger, IChecker, GetLastMergedIssue} from 'checker-core';

import {IJobSettings} from '../types/IJobSettings';

export class ExampleJob {
    readonly _checker: IChecker;

    constructor(settings: IJobSettings) {
        if (settings === void 0) {
            throw new Error('Has no Checker settings');
        }

        this._checker = new Checker(settings);

        Logger.info('-- ExampleJob:');
        Logger.info(this._checker.appName);
    }

    start() {
        const getLastMergedIssue = new GetLastMergedIssue();

        return getLastMergedIssue.run(this._checker);
    }
}

get-last-commit-issue


. Checker .


Examples


import {Checker, Logger, IChecker, GetLastCommitIssue} from 'checker-core';

import {IJobSettings} from '../types/IJobSettings';

export class ExampleJob {
    readonly _checker: IChecker;

    constructor(settings: IJobSettings) {
        if (settings === void 0) {
            throw new Error('Has no Checker settings');
        }

        this._checker = new Checker(settings);

        Logger.info('-- ExampleJob:');
        Logger.info(this._checker.appName);
    }

    start() {
        const getLastCommitIssue = new GetLastCommitIssue();

        return getLastCommitIssue.run(this._checker);
    }
}

send-notification send-smtp-notification


. .


send-notification send-smtp-notification — . smtp- IMailTransportSettings.
Examples


import {
    Checker,
    Logger,
    IChecker,
    IMailTransportSettings,
    IMailContent,
    SendSmtpNotification
} from 'checker-core';

import {IJobSettings} from '../types/IJobSettings';

export class ExampleJob {
    readonly _checker: IChecker;
    _emailSettings: IMailTransportSettings;

    constructor(settings: IJobSettings) {
        if (settings === void 0) {
            throw new Error('Has no Checker settings');
        }

        this._checker = new Checker(settings.checker);
        this._emailSettings = settings.email;

        Logger.info('-- ExampleJob:');
        Logger.info(this._checker.appName);
    }

    start() {
        const sendSmtpNotification = new SendSmtpNotification();
        const mailContent: IMailContent = {
            subject: '',
            content: ' , ?'
        };

        return sendSmtpNotification.run(this._checker, mailContent, this._emailSettings);
    }
}


: , .



Logger


.


Npm


Nexus.


mailTransport


( mail linux nodemailer smtp).


Decorators


logAsyncMethod


, .


process.env.IS_DEBUG_MODE , .


validate


. .


Examples
import {
    notNullProp, validate, required,
    validInfo, notNull, requiredProp
} from 'checker-core';

class TestSum {
    @validate
    static sum(
        @required
        @notNull
        @requiredProp(['a'])
        @notNullProp(['a'])
        @validInfo('a', Object)
        a: Object,
        @required
        @notNull
        @requiredProp(['b'])
        @notNullProp(['b'])
        @validInfo('b', Object)
        b: Object
    ) {
        return a.a + b.b;
    }
}

Errors


MissingRequiredArgument


.


MissingRequiredProperty


, .


NullArgument


, null.


NullProperty


, , null.


TypeMismatch


.


bundle-size-checker


checker-core core, :


/src —   
    |__ /bin —  , CLI     ,  2  :            `job`.         , ,    1 `process.exit(1)`;
    |__ /jobs — ,       (`checker` + ,   ),        .      `task`, `utils`,    ;
    |__ /tasks —      ; ,  ,  ,   ,  .    - `job`;
    |__ /models — ,   ,      `core`. ,      , , npm-.    `job`,  `tasks`   ,   `job`,   .  ,          `tasks`   `job`;
    |__ /types —  ;
    |__ /utils —   (, ,    (npm));
    |__ /errors — ,   ;
    |__ /enums —  ;
    |__ /decorators —    `utils`, `tasks`, `job`;
    |__ /declarations —  ,     Typescript.

, job — , , checker-, , , , - , , , ( , ..).


:


import path from 'path';
import {JiraSettings} from 'jira-manager';
import {
    Logger,
    JiraManagerSettings,
    IMailTransportSettings,
    MissingRequiredProperty
} from 'checker-core';
import {Module} from '../models/module';
import {CompareBundleSize} from '../tasks/compare-bundle-size';
import {Checker} from '../models';
import {IJobCheckBundleSizeSettings} from '../types/IJobCheckBundleSizeSettings';
import {IModule} from '../types/IModule';
import {IModuleSettings} from '../types/IModuleSettings';
import {GetMetaInfo} from '../tasks/get-meta-info';
import {CheckBundleSizeNotEmpty} from '../tasks/check-bundle-size-not-empty';
import {GetBundleSize} from '../tasks/get-bundle-size';
import {ReadFileError} from '../errors/ReadFileError';
import {IPackageJson} from '../types/IPackageJson';
import {IChecker} from '../types/IChecker';
import {FilterException} from '../tasks/filter-exception';

//      .
export class CheckBundleSize {
    readonly _checker: IChecker;
    readonly _jobName = 'CheckBundleSize';

    _moduleSettings: IModuleSettings;
    _checkerModule: IModule;
    _jiraSettings: JiraManagerSettings;
    _emailSettings: IMailTransportSettings;

    constructor(settings: IJobCheckBundleSizeSettings) {
        if (settings.checker === void 0) {
            throw new MissingRequiredProperty(this._jobName, 'constructor', 'settings', 'checker');
        }

        this._moduleSettings = settings.module;
        this._emailSettings = settings.emailSettings;
        this._jiraSettings = new JiraSettings(settings.jira);
        this._checker = new Checker(settings.checker);

        try {
            const packageJsonPath = path.resolve(this._checker.rootPath, 'package.json');
            const packageJson: IPackageJson = require(packageJsonPath);
            this._checkerModule = new Module(
                packageJson,
                this._moduleSettings.maxBundleSize,
                this._moduleSettings.needCheckBundleSize
            );
        } catch (err) {
            Logger.info('Can nott load package.json');
            throw new ReadFileError(this._jobName, 'package.json');
        }

        Logger.info(`${this._jobName}:`);
        Logger.info(this._checker.appName);
    }

    //     
    async start() {
        try {
            const getBundleSizeTask = new GetBundleSize();
            const getMetaInfo = new GetMetaInfo();
            const checkBundleSizeEmpty = new CheckBundleSizeNotEmpty();
            const compareBundleSize = new CompareBundleSize();

            const getBundleSize = await getBundleSizeTask.run(this._checker, this._checkerModule);

            if (!getBundleSize) {
                return;
            }

            await getMetaInfo.run(this._checker, this._jiraSettings);
            await checkBundleSizeEmpty.run(this._checker, this._checkerModule);

            await compareBundleSize.run(this._checker, this._checkerModule);
        } catch (err) {
            const filterException = new FilterException(this._emailSettings);
            await filterException.run(this._checker  v , this._checkerModule, err);

            throw err;
        }
    }
}

bin , cli .


const checkBundleSize = new CheckBundleSize(settings);

checkBundleSize.start()
    .then(() => {
        process.exit(0);
    })
    .catch((err: Error) => {
        console.error(err);
        console.error(`Can fail build: ${canFailBuild}`);
        if (canFailBuild) {
            process.exit(1);
        } else {
            process.exit(0);
        }
    });


/ , , (, , ). , : , , . roadmap . , — .


, .


. : ? , open source.


All Articles