UNPKG

6.03 kBPlain TextView Raw
1/* eslint-disable @typescript-eslint/camelcase */
2import fs from 'fs';
3import path from 'path';
4import semver from 'semver';
5import osenv from 'osenv';
6import spawn from 'cross-spawn';
7import _ from 'lodash';
8import DBInstance from '../../shared/db';
9import packageJson from '../../shared/packageJson';
10import { parseYaml } from '../../shared/yaml';
11import { setServerUrl } from '../../shared/git';
12import { UniversalPkg } from '../universal-pkg/dep/pkg';
13import {
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';
21import loggerInstance from '../logger';
22import {
23 getInstalledPlugins,
24 getLatestVersion,
25 getUniversalPluginVersion
26} from './utils';
27interface ErrorInstance {
28 name: string;
29 message: string;
30}
31
32const pkg = require('../../../package.json');
33const { getPkgInfo } = require('../native/install');
34const version = pkg.version;
35
36const { debug, silent } = process.env;
37const root = path.join(osenv.home(), FEFLOW_ROOT);
38const configPath = path.join(root, '.feflowrc.yml');
39const universalPkgPath = path.join(root, UNIVERSAL_PKG_JSON);
40const dbFile = path.join(root, UPDATE_COLLECTION);
41const db = new DBInstance(dbFile);
42const heartDBFile = path.join(root, HEART_BEAT_COLLECTION);
43const heartDB = new DBInstance(heartDBFile);
44heartDB.setAutoCompact(BEAT_GAP * 1000);
45const logger = loggerInstance({
46 debug: Boolean(debug),
47 silent: Boolean(silent)
48});
49
50// 设置特殊的静默进程名字
51process.title = 'feflow-update-beat-proccess';
52
53const 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
63const heartBeat = () => {
64 heartDB.update('beat_time', String(new Date().getTime()));
65};
66
67const 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
101const 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
155const 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 // eslint-disable-next-line
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// startBeat
199setInterval(heartBeat, BEAT_GAP);
200
201// queryCliUpdate
202setInterval(() => {
203 queryCliUpdate().catch(handleException);
204}, CHECK_UPDATE_GAP);
205
206// queryPluginsUpdate
207setInterval(() => {
208 queryPluginsUpdate().catch(handleException);
209}, CHECK_UPDATE_GAP);
210
211// queryUniversalPluginsUpdate
212setInterval(() => {
213 queryUniversalPluginsUpdate().catch(handleException);
214}, CHECK_UPDATE_GAP);