Alles auf einmal: automatische Überprüfung der Bündelgröße

Hallo allerseits, mein Name ist Ilya. Ich arbeite seit ungefähr 6 Jahren in der IT, die letzten 2 Jahre - in der Firma Yandex.Money als Front-End-Entwickler. Zu den Aufgaben gehören das Verwalten / Entwickeln von Teilen der Anwendungen im Moment des Projekts "Mein Konto" (und nein, es geht nicht nur darum, "die falsche Einrückung zu korrigieren", "die Farbe der Schaltfläche zu ändern" oder "die Form schnell zu tauschen").


In der Entwicklungsabteilung haben wir ein internes Challenges-Projekt - wir nennen Aufgaben, die nicht direkt mit den Hauptprodukten zusammenhängen. Dies sind Aufgaben, die sozial nützlich sind und zur Verbesserung der Infrastruktur, von CI / CD und mehr beitragen. Um beispielsweise die Bot-Benachrichtigung über Releases zu verfeinern, erstellen Sie ein Plug-In für IDEA zur selbstgenerierenden Bestätigung nach Objekt und überarbeiten Sie die alte UI-Bibliothek. Solche "Herausforderungen" können nach Belieben angenommen werden - sie eignen sich hervorragend zum Verwässern von Geschäftsaufgaben.


Vor langer Zeit habe ich mich vor langer Zeit für die Implementierung einer automatischen Überprüfung der Größe der Client-Bundle-Anwendung interessiert. Schließlich ist es besser, alles, was automatisch überprüft werden kann, automatisch zu überprüfen.


Das Ergebnis war natürlich nicht nur eine bestimmte implementierte Aufgabe, sondern eine Bibliothek mit Regeln und allgemeinen Funktionen (Kern / Kern) für die Implementierung von Prüfern (hoffe ich). Bei der Lösung des Problems waren die Zapfen voll, über die ich auch sprechen möchte.


Es gibt also Jenkins, Bitbucket, Mail (oder Telegramm, Slack - was Sie brauchen) und die Notwendigkeit, die Größe des Bundles zu überprüfen. Wir müssen all diese Freunde finden.


Bild


Es gibt 2 automatische Überprüfungen in Bitbucket für jede Pull-Anforderung, ohne die eine Zusammenführung nicht möglich ist: Die Aufgabe in Jira wird in den Status "Verifiziert" versetzt, und alle automatischen Überprüfungen in Jenkins werden erfolgreich gestartet und abgeschlossen. Gemäß dem in Bitbucket konfigurierten Hook erhält Jenkins Benachrichtigungen über einen Zweigwechsel, für den das gesamte Überprüfungsbündel gestartet wird. Job mit check (commit_checker) hat viel Pipeline:


  • Fusselskript starten;
  • AusfĂĽhren von Tests;
  • — , 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