Sekaligus: pemeriksaan otomatis ukuran bundel

Halo semuanya, nama saya Ilya. Saya telah bekerja di IT selama sekitar 6 tahun, 2 tahun terakhir - di perusahaan Yandex.Money sebagai pengembang front-end. Tanggung jawab termasuk memelihara / mengembangkan bagian-bagian aplikasi, saat ini proyek "Akun Saya" (dan tidak, itu bukan hanya "memperbaiki indentasi yang salah", "mengubah warna tombol" atau "dengan cepat membentuk bentuk").


Di departemen pengembangan, kami memiliki proyek Tantangan internal - kami menyebut tugas yang tidak terkait langsung dengan produk utama. Ini adalah tugas yang akan bermanfaat secara sosial dan akan membantu meningkatkan infrastruktur, CI / CD, dan banyak lagi. Misalnya, untuk menyaring bot yang memberi tahu tentang rilis, buat plug-in untuk IDEA untuk objek penghasil otomatis oleh objek, dan perbaiki pustaka UI lama. "Tantangan" seperti itu dapat diambil sesuka hati - mereka bagus untuk melemahkan tugas bisnis.


Pada satu titik, saya mengambil waktu yang lama bahwa saya tertarik pada tugas menerapkan verifikasi otomatis ukuran aplikasi bundel klien. Lagi pula, semua yang dapat diperiksa secara otomatis lebih baik untuk diperiksa secara otomatis.


Secara alami, hasilnya bukan hanya tugas yang diimplementasikan spesifik, tetapi perpustakaan dengan aturan dan fungsi umum (inti / inti) untuk mengimplementasikan catur apa pun (saya harap). Dalam proses penyelesaian masalah, kerucut penuh yang juga ingin saya bicarakan.


Jadi, ada Jenkins, Bitbucket, mail (atau Telegram, Slack - apa yang Anda butuhkan) dan kebutuhan untuk memeriksa ukuran bundel. Kita perlu membuat semua teman ini.


gambar


Ada 2 pemeriksaan otomatis di Bitbucket untuk setiap permintaan tarik, yang tanpanya penggabungan menjadi mustahil: tugas di Jira ditransfer ke status "Terverifikasi", dan semua pemeriksaan otomatis di Jenkins dimulai dan diselesaikan dengan sukses. Lebih lanjut, sesuai dengan kait yang dikonfigurasi dalam Bitbucket, Jenkins menerima pemberitahuan tentang perubahan cabang, di mana seluruh bundel cek diluncurkan. Pekerjaan dengan cek (commit_checker) memiliki banyak saluran pipa:


  • luncurkan skrip serat;
  • menjalankan tes;
  • โ€” , 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