mirror of
https://github.com/whyour/qinglong.git
synced 2025-05-23 14:56:07 +08:00
增加依赖是否已经安装判断
This commit is contained in:
parent
f3791cbb62
commit
b69ff2895e
|
@ -399,6 +399,18 @@ export function promiseExec(command: string): Promise<string> {
|
|||
});
|
||||
}
|
||||
|
||||
export function promiseExecSuccess(command: string): Promise<string> {
|
||||
return new Promise((resolve) => {
|
||||
exec(
|
||||
command,
|
||||
{ maxBuffer: 200 * 1024 * 1024, encoding: 'utf8' },
|
||||
(err, stdout, stderr) => {
|
||||
resolve(stdout || '');
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function parseHeaders(headers: string) {
|
||||
if (!headers) return {};
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@ import { DataTypes, Model, ModelDefined } from 'sequelize';
|
|||
export class Dependence {
|
||||
timestamp?: string;
|
||||
id?: number;
|
||||
status?: DependenceStatus;
|
||||
type?: DependenceTypes;
|
||||
name?: number;
|
||||
status: DependenceStatus;
|
||||
type: DependenceTypes;
|
||||
name: string;
|
||||
log?: string[];
|
||||
remark?: string;
|
||||
|
||||
|
@ -42,13 +42,25 @@ export enum DependenceTypes {
|
|||
|
||||
export enum InstallDependenceCommandTypes {
|
||||
'pnpm add -g',
|
||||
'pip3 install',
|
||||
'pip3 install --disable-pip-version-check --root-user-action=ignore',
|
||||
'apk add',
|
||||
}
|
||||
|
||||
export enum GetDependenceCommandTypes {
|
||||
'pnpm ls -g ',
|
||||
'pip3 list --disable-pip-version-check --root-user-action=ignore',
|
||||
'apk info',
|
||||
}
|
||||
|
||||
export enum versionDependenceCommandTypes {
|
||||
'@',
|
||||
'==',
|
||||
'=',
|
||||
}
|
||||
|
||||
export enum unInstallDependenceCommandTypes {
|
||||
'pnpm remove -g',
|
||||
'pip3 uninstall -y',
|
||||
'pip3 uninstall --disable-pip-version-check --root-user-action=ignore -y',
|
||||
'apk del',
|
||||
}
|
||||
|
||||
|
|
|
@ -8,11 +8,13 @@ import {
|
|||
DependenceTypes,
|
||||
unInstallDependenceCommandTypes,
|
||||
DependenceModel,
|
||||
GetDependenceCommandTypes,
|
||||
versionDependenceCommandTypes,
|
||||
} from '../data/dependence';
|
||||
import { spawn } from 'cross-spawn';
|
||||
import SockService from './sock';
|
||||
import { FindOptions, Op } from 'sequelize';
|
||||
import { concurrentRun } from '../config/util';
|
||||
import { promiseExecSuccess } from '../config/util';
|
||||
import dayjs from 'dayjs';
|
||||
import taskLimit from '../shared/pLimit';
|
||||
|
||||
|
@ -137,9 +139,17 @@ export default class DependenceService {
|
|||
}
|
||||
|
||||
private async updateLog(ids: number[], log: string): Promise<void> {
|
||||
const doc = await DependenceModel.findOne({ where: { id: ids } });
|
||||
taskLimit.updateDepLog(async () => {
|
||||
const docs = await DependenceModel.findAll({ where: { id: ids } });
|
||||
for (const doc of docs) {
|
||||
const newLog = doc?.log ? [...doc.log, log] : [log];
|
||||
await DependenceModel.update({ log: newLog }, { where: { id: ids } });
|
||||
await DependenceModel.update(
|
||||
{ log: newLog },
|
||||
{ where: { id: doc.id } },
|
||||
);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public installOrUninstallDependency(
|
||||
|
@ -155,7 +165,7 @@ export default class DependenceService {
|
|||
: DependenceStatus.removing;
|
||||
await DependenceModel.update({ status }, { where: { id: depIds } });
|
||||
|
||||
const socketMessageType = !force
|
||||
const socketMessageType = isInstall
|
||||
? 'installDependence'
|
||||
: 'uninstallDependence';
|
||||
const depName = dependency.name;
|
||||
|
@ -163,7 +173,7 @@ export default class DependenceService {
|
|||
isInstall
|
||||
? InstallDependenceCommandTypes
|
||||
: unInstallDependenceCommandTypes
|
||||
)[dependency.type as any];
|
||||
)[dependency.type];
|
||||
const actionText = isInstall ? '安装' : '删除';
|
||||
const startTime = dayjs();
|
||||
|
||||
|
@ -175,7 +185,39 @@ export default class DependenceService {
|
|||
message,
|
||||
references: depIds,
|
||||
});
|
||||
await this.updateLog(depIds, message);
|
||||
this.updateLog(depIds, message);
|
||||
|
||||
// 判断是否已经安装过依赖
|
||||
if (isInstall) {
|
||||
const getCommandPrefix = GetDependenceCommandTypes[dependency.type];
|
||||
const depVersionStr = versionDependenceCommandTypes[dependency.type];
|
||||
const [_depName] = dependency.name.split(depVersionStr);
|
||||
const depInfo = (
|
||||
await promiseExecSuccess(
|
||||
dependency.type === DependenceTypes.linux
|
||||
? `${getCommandPrefix} ${_depName}`
|
||||
: `${getCommandPrefix} | grep "${_depName}"`,
|
||||
)
|
||||
).replace(/\s{2,}/, ' ');
|
||||
|
||||
if (depInfo) {
|
||||
const endTime = dayjs();
|
||||
const _message = `检测到已经安装 ${_depName}\n\n${depInfo}\n跳过安装\n\n依赖${actionText}成功,结束时间 ${endTime.format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)},耗时 ${endTime.diff(startTime, 'second')} 秒`;
|
||||
this.sockService.sendMessage({
|
||||
type: socketMessageType,
|
||||
message: _message,
|
||||
references: depIds,
|
||||
});
|
||||
this.updateLog(depIds, _message);
|
||||
await DependenceModel.update(
|
||||
{ status: DependenceStatus.installed },
|
||||
{ where: { id: depIds } },
|
||||
);
|
||||
return resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
const cp = spawn(`${depRunCommand} ${depName}`, {
|
||||
shell: '/bin/bash',
|
||||
|
@ -187,7 +229,7 @@ export default class DependenceService {
|
|||
message: data.toString(),
|
||||
references: depIds,
|
||||
});
|
||||
await this.updateLog(depIds, data.toString());
|
||||
this.updateLog(depIds, data.toString());
|
||||
});
|
||||
|
||||
cp.stderr.on('data', async (data) => {
|
||||
|
@ -196,7 +238,7 @@ export default class DependenceService {
|
|||
message: data.toString(),
|
||||
references: depIds,
|
||||
});
|
||||
await this.updateLog(depIds, data.toString());
|
||||
this.updateLog(depIds, data.toString());
|
||||
});
|
||||
|
||||
cp.on('error', async (err) => {
|
||||
|
@ -205,7 +247,7 @@ export default class DependenceService {
|
|||
message: JSON.stringify(err),
|
||||
references: depIds,
|
||||
});
|
||||
await this.updateLog(depIds, JSON.stringify(err));
|
||||
this.updateLog(depIds, JSON.stringify(err));
|
||||
});
|
||||
|
||||
cp.on('close', async (code) => {
|
||||
|
@ -221,7 +263,7 @@ export default class DependenceService {
|
|||
message,
|
||||
references: depIds,
|
||||
});
|
||||
await this.updateLog(depIds, message);
|
||||
this.updateLog(depIds, message);
|
||||
|
||||
let status = null;
|
||||
if (isSucceed) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import { AuthDataType, AuthModel } from "../data/auth";
|
|||
|
||||
class TaskLimit {
|
||||
private oneLimit = pLimit(1);
|
||||
private updateLogLimit = pLimit(1);
|
||||
private cpuLimit = pLimit(Math.max(os.cpus().length, 4));
|
||||
|
||||
constructor() {
|
||||
|
@ -22,15 +23,15 @@ class TaskLimit {
|
|||
}
|
||||
|
||||
public runWithCpuLimit<T>(fn: () => Promise<T>): Promise<T> {
|
||||
return this.cpuLimit(() => {
|
||||
return fn();
|
||||
});
|
||||
return this.cpuLimit(fn);
|
||||
}
|
||||
|
||||
public runOneByOne<T>(fn: () => Promise<T>): Promise<T> {
|
||||
return this.oneLimit(() => {
|
||||
return fn();
|
||||
});
|
||||
return this.oneLimit(fn);
|
||||
}
|
||||
|
||||
public updateDepLog<T>(fn: () => Promise<T>): Promise<T> {
|
||||
return this.updateLogLimit(fn);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ import DependenceLogModal from './logModal';
|
|||
import { useOutletContext } from '@umijs/max';
|
||||
import { SharedContext } from '@/layouts';
|
||||
import useTableScrollHeight from '@/hooks/useTableScrollHeight';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const { Text } = Typography;
|
||||
const { Search } = Input;
|
||||
|
@ -123,27 +124,20 @@ const Dependence = () => {
|
|||
dataIndex: 'remark',
|
||||
key: 'remark',
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
key: 'updatedAt',
|
||||
dataIndex: 'updatedAt',
|
||||
render: (text: string) => {
|
||||
return <span>{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'timestamp',
|
||||
dataIndex: 'timestamp',
|
||||
render: (text: string, record: any) => {
|
||||
const language = navigator.language || navigator.languages[0];
|
||||
const time = record.createdAt || record.timestamp;
|
||||
const date = new Date(time)
|
||||
.toLocaleString(language, {
|
||||
hour12: false,
|
||||
})
|
||||
.replace(' 24:', ' 00:');
|
||||
return (
|
||||
<Tooltip
|
||||
placement="topLeft"
|
||||
title={date}
|
||||
trigger={['hover', 'click']}
|
||||
>
|
||||
<span>{date}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
key: 'createdAt',
|
||||
dataIndex: 'createdAt',
|
||||
render: (text: string) => {
|
||||
return <span>{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -122,7 +122,7 @@ const DependenceModal = ({
|
|||
name="name"
|
||||
label="名称"
|
||||
rules={[
|
||||
{ required: true, message: '请输入依赖名称', whitespace: true },
|
||||
{ required: true, message: '请输入依赖名称,支持指定版本', whitespace: true },
|
||||
]}
|
||||
>
|
||||
<Input.TextArea
|
||||
|
|
Loading…
Reference in New Issue
Block a user