src/lib/Application.ts   A
last analyzed

Complexity

Total Complexity 34
Complexity/F 4.86

Size

Lines of Code 230
Function Count 7

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 167
dl 0
loc 230
rs 9.68
c 0
b 0
f 0
wmc 34
mnd 27
bc 27
fnc 7
bpm 3.8571
cpm 4.8571
noi 0

7 Functions

Rating   Name   Duplication   Size   Complexity  
A Application.createQueue 0 30 1
B Application.executeRun 0 43 5
B Application.handleQueuedRepos 0 30 6
B Application.executeInit 0 35 8
B Application.init 0 23 7
A Application.initCodeBoost 0 8 1
B Application.getConfigFilename 0 28 6
1
import { AppSettings, loadSettings } from '@/lib/AppSettings';
2
import { CodeBoost } from '@/lib/CodeBoost';
3
import { initOctokit } from '@/lib/github';
4
import { HistoryManager } from '@/lib/HistoryManager';
5
import { Repository } from '@/lib/Repository';
6
import { existsSync, mkdirSync, writeFileSync } from 'fs';
7
import { userInfo } from 'os';
8
import { BatchManager } from '@/lib/BatchManager';
9
import Queue from 'better-queue';
10
import chalk from 'chalk';
11
12
export class Application {
13
    public settings!: AppSettings;
14
    public repositoryName!: string;
15
    public historyManager!: HistoryManager;
16
17
    constructor(public configFilename?: string, public specifiedConfigFilename = false) {
18
        if (!configFilename) {
19
            this.configFilename = 'codeboost.config.js';
20
        }
21
    }
22
23
    async init(options: Record<string, any> = {}) {
24
        this.configFilename = this.getConfigFilename() ?? '';
25
26
        if (!this.configFilename) {
27
            console.log(`${chalk.redBright('✗')} No config file found. Please run '${chalk.cyanBright('codeboost init')}'.`);
28
            // eslint-disable-next-line no-process-exit
29
            process.exit(1);
30
        }
31
32
        const settings = loadSettings(this.configFilename);
33
34
        if (typeof options.dryRun === 'boolean') {
35
            settings.dry_run = options.dryRun;
36
        }
37
38
        initOctokit(settings.github_token);
39
40
        if (!this.historyManager) {
41
            this.historyManager = new HistoryManager(options.historyFn);
42
        }
43
44
        return settings;
45
    }
46
47
    public async initCodeBoost(repoName: string, settings: AppSettings) {
48
        const repo = new Repository(repoName, settings.repository_storage_path);
49
50
        const codeboost = new CodeBoost(settings, this.historyManager);
51
        await codeboost.init(repo, settings);
52
53
        return codeboost;
54
    }
55
56
    public getConfigFilename(homePath: string | null = null) {
57
        homePath = homePath ?? userInfo({ encoding: 'utf8' }).homedir;
58
        const cwd = process.cwd();
59
60
        // Use specified config filename if provided
61
        if (this.specifiedConfigFilename) {
62
            return this.configFilename;
63
        }
64
65
        // Otherwise, try to find the config file in the following locations, in order:
66
        const configFilenames = [
67
            `${homePath}/.codeboost/codeboost.config.js`,
68
            `${homePath}/codeboost.config.js`,
69
            `${cwd}/codeboost.config.js`,
70
            `${cwd}/${this.configFilename}`,
71
        ];
72
73
        let result = this.configFilename;
74
75
        for (const filename of configFilenames) {
76
            if (existsSync(filename) && filename.endsWith('.js')) {
77
                result = filename;
78
            }
79
        }
80
81
        // If the config file is not found in any of the above locations, return the default value
82
        return result;
83
    }
84
85
    async executeRun(repoName: string, boostName: string, options: Record<string, any>) {
86
        const homePath = userInfo({ encoding: 'utf8' }).homedir;
87
        options.historyFn = `${homePath}/.codeboost/history.json`;
88
89
        // Set historyFn to empty string if the file does not exist
90
        if (!existsSync(options.historyFn)) {
91
            writeFileSync(options.historyFn, JSON.stringify([]));
92
        }
93
94
        const settings = await this.init(options);
95
96
        if (options.dryRun) {
97
            console.log(`${chalk.yellowBright('dry-run mode enabled')}`);
98
            settings.dry_run = true;
99
        }
100
101
        const runCodeBoost = async (repoName: string) => {
102
            if (!repoName) {
103
                return;
104
            }
105
            try {
106
                const codeboost = await this.initCodeBoost(repoName, settings);
107
108
                await codeboost.prepareRepository();
109
                await codeboost.runBoost(boostName, [ '8.2' ]);
110
            } catch (e: any) {
111
                console.log(`${chalk.redBright('✗')} error: ${e.message}`);
112
            }
113
114
            this.historyManager.save();
115
        };
116
117
        const complete = { value: false };
118
        const queue = this.createQueue(runCodeBoost, new Proxy(complete, {}));
119
120
        await this.handleQueuedRepos({
121
            queue,
122
            repoName,
123
            options,
124
            boostName,
125
            settings,
126
            complete,
127
        });
128
    }
129
130
    async handleQueuedRepos({
131
        queue, repoName, options, boostName, settings, complete 
132
    }: any) {
133
        if (options.batch) {
134
            repoName = 'temp/temp';
135
        }
136
137
        const codeboost = await this.initCodeBoost(repoName, settings);
138
        const batchMgr = new BatchManager(options.batch, codeboost);
139
140
        if (!options.batch) {
141
            batchMgr.insertBatch(repoName);
142
            options.size = 1;
143
        }
144
145
        const batch = batchMgr.getBatch(boostName, options.size);
146
147
        if (batch.length === 0) {
148
            console.log(`${chalk.redBright('✗')} No repositories found in batch.`);
149
            return;
150
        }
151
152
        batch.forEach(item => queue.push(item.name));
153
154
        while (!complete.value) {
155
            await new Promise(resolve => setTimeout(resolve, 100));
156
157
            if (complete.value) {
158
                break;
159
            }
160
        }
161
    }
162
163
    async executeInit(homePath: string | null = null) {
164
        homePath = homePath ?? userInfo({ encoding: 'utf8' }).homedir;
165
        let configFn = `${homePath}/.codeboost/codeboost.config.js`;
166
167
        if (this.specifiedConfigFilename) {
168
            configFn = this.configFilename ?? '';
169
        }
170
171
        if (!existsSync(`${homePath}/.codeboost`)) {
172
            mkdirSync(`${homePath}/.codeboost`);
173
            console.log(`${chalk.greenBright('✓')} config directory created`);
174
        }
175
176
        if (!existsSync(configFn)) {
177
            writeFileSync(
178
                configFn,
179
                `
180
            module.exports.default = {
181
                github_token: '$GITHUB_TOKEN',
182
                repository_storage_path: \`\${__dirname}/repositories\`,
183
                boosts_path: \`\${__dirname}/boosts\`,
184
                use_forks: true,
185
                use_pull_requests: true,
186
                log_target: 'console',
187
            };`
188
                    .replaceAll('            ', '')
189
                    .trim(),
190
                { encoding: 'utf8' },
191
            );
192
193
            console.log(`${chalk.greenBright('✓')} global config file created`);
194
        }
195
196
        console.log(`${chalk.greenBright('✓')} codeboost initialized`);
197
    }
198
199
    public createQueue(runCodeBoost, complete) {
200
        const queue = new Queue(
201
            async (r, cb) => {
202
                await runCodeBoost(r);
203
                cb();
204
            },
205
            { concurrent: 2, maxRetries: 2, retryDelay: 1000 },
206
        );
207
208
        queue.on('task_finish', function (taskId, result) {
209
            //console.log(`${chalk.greenBright('✓')} ${taskId} finished`);
210
        });
211
        queue.on('empty', () => {
212
            //console.log('empty');
213
        });
214
        queue.on('drain', function () {
215
            complete.value = true;
216
        });
217
        queue.on('task_queued', t => {
218
            //console.log('task_queued', t);
219
        });
220
        queue.on('task_started', t => {
221
            //console.log('task_started', t);
222
        });
223
        queue.on('task_failed', t => {
224
            //console.log('task_accepted', t);
225
        });
226
227
        return queue;
228
    }
229
}
230