1 |
|
2 | import fs from 'fs';
|
3 | import path from 'path';
|
4 | import semver from 'semver';
|
5 | import osenv from 'osenv';
|
6 | import spawn from 'cross-spawn';
|
7 | import _ from 'lodash';
|
8 | import DBInstance from '../../shared/db';
|
9 | import packageJson from '../../shared/packageJson';
|
10 | import { parseYaml } from '../../shared/yaml';
|
11 | import { setServerUrl } from '../../shared/git';
|
12 | import { UniversalPkg } from '../universal-pkg/dep/pkg';
|
13 | import {
|
14 | HEART_BEAT_COLLECTION,
|
15 | UPDATE_COLLECTION,
|
16 | BEAT_GAP,
|
17 | CHECK_UPDATE_GAP,
|
18 | FEFLOW_ROOT,
|
19 | UNIVERSAL_PKG_JSON
|
20 | } from '../../shared/constant';
|
21 | import loggerInstance from '../logger';
|
22 | import {
|
23 | getInstalledPlugins,
|
24 | getLatestVersion,
|
25 | getUniversalPluginVersion
|
26 | } from './utils';
|
27 | interface ErrorInstance {
|
28 | name: string;
|
29 | message: string;
|
30 | }
|
31 |
|
32 | const pkg = require('../../../package.json');
|
33 | const { getPkgInfo } = require('../native/install');
|
34 | const version = pkg.version;
|
35 |
|
36 | const { debug, silent } = process.env;
|
37 | const root = path.join(osenv.home(), FEFLOW_ROOT);
|
38 | const configPath = path.join(root, '.feflowrc.yml');
|
39 | const universalPkgPath = path.join(root, UNIVERSAL_PKG_JSON);
|
40 | const dbFile = path.join(root, UPDATE_COLLECTION);
|
41 | const db = new DBInstance(dbFile);
|
42 | const heartDBFile = path.join(root, HEART_BEAT_COLLECTION);
|
43 | const heartDB = new DBInstance(heartDBFile);
|
44 | heartDB.setAutoCompact(BEAT_GAP * 1000);
|
45 | const logger = loggerInstance({
|
46 | debug: Boolean(debug),
|
47 | silent: Boolean(silent)
|
48 | });
|
49 |
|
50 |
|
51 | process.title = 'feflow-update-beat-proccess';
|
52 |
|
53 | const handleException = (e: ErrorInstance): void => {
|
54 | db.update('exception', `${e.name}: ${e.message}`).then(() => {
|
55 | process.exit(1);
|
56 | });
|
57 | };
|
58 |
|
59 | (process as NodeJS.EventEmitter).on('uncaughtException', handleException);
|
60 |
|
61 | (process as NodeJS.EventEmitter).on('unhandledRejection', handleException);
|
62 |
|
63 | const heartBeat = () => {
|
64 | heartDB.update('beat_time', String(new Date().getTime()));
|
65 | };
|
66 |
|
67 | const queryCliUpdate = async () => {
|
68 | const config = parseYaml(configPath);
|
69 |
|
70 | if (!config) {
|
71 | return;
|
72 | }
|
73 | if (
|
74 | config['lastUpdateCheck'] &&
|
75 | +new Date() - parseInt(config['lastUpdateCheck'], 10) <= 1000 * 3600 * 24
|
76 | ) {
|
77 | return;
|
78 | }
|
79 |
|
80 | if (config['autoUpdate'] !== 'true') {
|
81 | return;
|
82 | }
|
83 |
|
84 | const latestVersion: any = await getLatestVersion(
|
85 | '@feflow/cli',
|
86 | config['packageManager']
|
87 | );
|
88 | if (latestVersion && semver.gt(latestVersion, version)) {
|
89 | let updateData: any = await db.read('update_data');
|
90 | updateData = updateData?.['value'];
|
91 | if (updateData.latest_cli_version !== latestVersion) {
|
92 | const newUpdateData = {
|
93 | ...updateData,
|
94 | latest_cli_version: latestVersion
|
95 | };
|
96 | await db.update('update_data', newUpdateData);
|
97 | }
|
98 | }
|
99 | };
|
100 |
|
101 | const queryPluginsUpdate = async () => {
|
102 | const config = parseYaml(configPath);
|
103 | if (!config) {
|
104 | return;
|
105 | }
|
106 |
|
107 | Promise.all(
|
108 | getInstalledPlugins().map(async (name: any) => {
|
109 | const pluginPath = path.join(root, 'node_modules', name, 'package.json');
|
110 | const content: any = fs.readFileSync(pluginPath);
|
111 | const pkg: any = JSON.parse(content);
|
112 | const localVersion = pkg.version;
|
113 | const registryUrl = spawn
|
114 | .sync(config['packageManager'], ['config', 'get', 'registry'], {
|
115 | windowsHide: true
|
116 | })
|
117 | .stdout.toString()
|
118 | .replace(/\n/, '')
|
119 | .replace(/\/$/, '');
|
120 | const latestVersion = await packageJson(name, registryUrl).catch(
|
121 | (err: any) => {
|
122 | logger.debug('Check plugin update error', err);
|
123 | }
|
124 | );
|
125 |
|
126 | if (latestVersion && semver.gt(latestVersion, localVersion)) {
|
127 | return {
|
128 | name,
|
129 | latestVersion,
|
130 | localVersion
|
131 | };
|
132 | } else {
|
133 | logger.debug('All plugins is in latest version');
|
134 | }
|
135 | })
|
136 | ).then(async (plugins: any) => {
|
137 | plugins = plugins.filter((plugin: any) => {
|
138 | return plugin && plugin.name;
|
139 | });
|
140 | logger.debug('tnpm plugins update infomation', plugins);
|
141 | if (plugins.length) {
|
142 | let updateData: any = await db.read('update_data');
|
143 | updateData = updateData?.['value'];
|
144 | if (!_.isEqual(updateData.latest_plugins, plugins)) {
|
145 | const newUpdateData = {
|
146 | ...updateData,
|
147 | latest_plugins: plugins
|
148 | };
|
149 | await db.update('update_data', newUpdateData);
|
150 | }
|
151 | }
|
152 | });
|
153 | };
|
154 |
|
155 | const queryUniversalPluginsUpdate = async () => {
|
156 | const config = parseYaml(configPath);
|
157 | if (!config || !config['serverUrl']) {
|
158 | return;
|
159 | }
|
160 | setServerUrl(config['serverUrl']);
|
161 |
|
162 | const universalPkg = new UniversalPkg(universalPkgPath);
|
163 | const latestUniversalPlugins: any[] = [];
|
164 |
|
165 |
|
166 | for (const [pkg, version] of universalPkg.getInstalled()) {
|
167 | const pkgInfo = await getPkgInfo(
|
168 | { root, config, logger },
|
169 | `${pkg}@${version}`
|
170 | ).catch((e: string) => {
|
171 | db.insertOnce('update_error', `${pkg}@${version}`, e);
|
172 | });
|
173 | if (!pkgInfo) {
|
174 | continue;
|
175 | }
|
176 | const versionObj = await getUniversalPluginVersion(pkgInfo, universalPkg);
|
177 | if (versionObj.latestVersion) {
|
178 | latestUniversalPlugins.push(versionObj);
|
179 | }
|
180 | }
|
181 |
|
182 | logger.debug('universal plugins update infomation', latestUniversalPlugins);
|
183 | if (latestUniversalPlugins.length) {
|
184 | let updateData: any = await db.read('update_data');
|
185 | updateData = updateData?.['value'];
|
186 | if (
|
187 | !_.isEqual(updateData.latest_universal_plugins, latestUniversalPlugins)
|
188 | ) {
|
189 | const newUpdateData = {
|
190 | ...updateData,
|
191 | latest_universal_plugins: latestUniversalPlugins
|
192 | };
|
193 | await db.update('update_data', newUpdateData);
|
194 | }
|
195 | }
|
196 | };
|
197 |
|
198 |
|
199 | setInterval(heartBeat, BEAT_GAP);
|
200 |
|
201 |
|
202 | setInterval(() => {
|
203 | queryCliUpdate().catch(handleException);
|
204 | }, CHECK_UPDATE_GAP);
|
205 |
|
206 |
|
207 | setInterval(() => {
|
208 | queryPluginsUpdate().catch(handleException);
|
209 | }, CHECK_UPDATE_GAP);
|
210 |
|
211 |
|
212 | setInterval(() => {
|
213 | queryUniversalPluginsUpdate().catch(handleException);
|
214 | }, CHECK_UPDATE_GAP);
|