Tudo de uma vez: verificação automática do tamanho do pacote

Olá pessoal, meu nome é Ilya. Trabalho com TI há 6 anos, nos últimos 2 anos - na empresa Yandex.Money como desenvolvedor front-end. As responsabilidades incluem manter / desenvolver partes dos aplicativos, no momento o projeto "Minha conta" (e não, não é apenas "corrigir o recuo errado", "alterar a cor do botão" ou "trocar rapidamente a forma").


No departamento de desenvolvimento, temos um projeto interno de Desafios - denominamos tarefas que não estão diretamente relacionadas aos principais produtos. Essas são tarefas que serão socialmente úteis e ajudarão a melhorar a infraestrutura, o CI / CD e muito mais. Por exemplo, para refinar o bot que notifica sobre liberações, crie um plug-in para o IDEA para auto-geração de declaração por objeto e refatorar a antiga biblioteca de interface do usuário. Esses "desafios" podem ser tomados à vontade - eles são ótimos para diluir tarefas de negócios.


Em um ponto, demorei muito tempo atrás que estava interessado na tarefa de implementar a verificação automática do tamanho do pacote configurável do cliente do aplicativo. Afinal, tudo o que pode ser verificado automaticamente é melhor verificar automaticamente.


Naturalmente, o resultado não foi apenas uma tarefa implementada específica, mas uma biblioteca com regras e funções gerais (core / core) para implementar quaisquer verificadores (espero). No processo de solução do problema, os cones estavam cheios dos quais também quero falar.


Portanto, existem Jenkins, Bitbucket, mail (ou Telegram, Slack - o que você precisa) e a necessidade de verificar o tamanho do pacote. Precisamos fazer todos esses amigos.


imagem


Existem 2 verificações automáticas no Bitbucket para cada solicitação pull, sem as quais a mesclagem é impossível: a tarefa no Jira é transferida para o status “Verificado” e todas as verificações automáticas no Jenkins são iniciadas e concluídas com êxito. Além disso, de acordo com o gancho configurado no Bitbucket, Jenkins recebe notificações sobre uma alteração de ramificação para a qual todo o pacote de verificações é iniciado. O trabalho com cheque (commit_checker) possui muitos pipeline:


  • lançar script de cotão;
  • executando testes;
  • — , 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