diff --git a/spug_api/apps/deploy/utils.py b/spug_api/apps/deploy/utils.py index 199aa96..1ea4426 100644 --- a/spug_api/apps/deploy/utils.py +++ b/spug_api/apps/deploy/utils.py @@ -158,44 +158,44 @@ def _deploy_ext1_host(req, helper, h_id, env): if not host: helper.send_error(h_id, 'no such host') env.update({'SPUG_HOST_ID': h_id, 'SPUG_HOST_NAME': host.hostname}) - ssh = host.get_ssh() - code, _ = ssh.exec_command( - f'mkdir -p {extend.dst_repo} && [ -e {extend.dst_dir} ] && [ ! -L {extend.dst_dir} ]') - if code == 0: - helper.send_error(host.id, f'检测到该主机的发布目录 {extend.dst_dir!r} 已存在,为了数据安全请自行备份后删除该目录,Spug 将会创建并接管该目录。') - # clean - clean_command = f'ls -d {extend.deploy_id}_* 2> /dev/null | sort -t _ -rnk2 | tail -n +{extend.versions + 1} | xargs rm -rf' - helper.remote(host.id, ssh, f'cd {extend.dst_repo} && rm -rf {req.spug_version} && {clean_command}') - # transfer files - tar_gz_file = f'{req.spug_version}.tar.gz' - try: - ssh.put_file(os.path.join(REPOS_DIR, 'build', tar_gz_file), os.path.join(extend.dst_repo, tar_gz_file)) - except Exception as e: - helper.send_error(host.id, f'exception: {e}') + with host.get_ssh(default_env=env) as ssh: + code, _ = ssh.exec_command_raw( + f'mkdir -p {extend.dst_repo} && [ -e {extend.dst_dir} ] && [ ! -L {extend.dst_dir} ]') + if code == 0: + helper.send_error(host.id, f'检测到该主机的发布目录 {extend.dst_dir!r} 已存在,为了数据安全请自行备份后删除该目录,Spug 将会创建并接管该目录。') + # clean + clean_command = f'ls -d {extend.deploy_id}_* 2> /dev/null | sort -t _ -rnk2 | tail -n +{extend.versions + 1} | xargs rm -rf' + helper.remote_raw(host.id, ssh, f'cd {extend.dst_repo} && rm -rf {req.spug_version} && {clean_command}') + # transfer files + tar_gz_file = f'{req.spug_version}.tar.gz' + try: + ssh.put_file(os.path.join(REPOS_DIR, 'build', tar_gz_file), os.path.join(extend.dst_repo, tar_gz_file)) + except Exception as e: + helper.send_error(host.id, f'exception: {e}') - command = f'cd {extend.dst_repo} && tar xf {tar_gz_file} && rm -f {req.deploy_id}_*.tar.gz' - helper.remote(host.id, ssh, command) - helper.send_step(h_id, 1, '完成\r\n') + command = f'cd {extend.dst_repo} && tar xf {tar_gz_file} && rm -f {req.deploy_id}_*.tar.gz' + helper.remote_raw(host.id, ssh, command) + helper.send_step(h_id, 1, '完成\r\n') - # pre host - repo_dir = os.path.join(extend.dst_repo, req.spug_version) - if extend.hook_pre_host: - helper.send_step(h_id, 2, f'{human_time()} 发布前任务... \r\n') - command = f'cd {repo_dir} ; {extend.hook_pre_host}' - helper.remote(host.id, ssh, command, env) + # pre host + repo_dir = os.path.join(extend.dst_repo, req.spug_version) + if extend.hook_pre_host: + helper.send_step(h_id, 2, f'{human_time()} 发布前任务... \r\n') + command = f'cd {repo_dir} ; {extend.hook_pre_host}' + helper.remote(host.id, ssh, command) - # do deploy - helper.send_step(h_id, 3, f'{human_time()} 执行发布... ') - helper.remote(host.id, ssh, f'rm -f {extend.dst_dir} && ln -sfn {repo_dir} {extend.dst_dir}') - helper.send_step(h_id, 3, '完成\r\n') + # do deploy + helper.send_step(h_id, 3, f'{human_time()} 执行发布... ') + helper.remote_raw(host.id, ssh, f'rm -f {extend.dst_dir} && ln -sfn {repo_dir} {extend.dst_dir}') + helper.send_step(h_id, 3, '完成\r\n') - # post host - if extend.hook_post_host: - helper.send_step(h_id, 4, f'{human_time()} 发布后任务... \r\n') - command = f'cd {extend.dst_dir} ; {extend.hook_post_host}' - helper.remote(host.id, ssh, command, env) + # post host + if extend.hook_post_host: + helper.send_step(h_id, 4, f'{human_time()} 发布后任务... \r\n') + command = f'cd {extend.dst_dir} ; {extend.hook_post_host}' + helper.remote(host.id, ssh, command) - helper.send_step(h_id, 100, f'\r\n{human_time()} ** 发布成功 **') + helper.send_step(h_id, 100, f'\r\n{human_time()} ** 发布成功 **') def _deploy_ext2_host(helper, h_id, actions, env, spug_version): @@ -204,31 +204,31 @@ def _deploy_ext2_host(helper, h_id, actions, env, spug_version): if not host: helper.send_error(h_id, 'no such host') env.update({'SPUG_HOST_ID': h_id, 'SPUG_HOST_NAME': host.hostname}) - ssh = host.get_ssh() - for index, action in enumerate(actions): - helper.send_step(h_id, 1 + index, f'{human_time()} {action["title"]}...\r\n') - if action.get('type') == 'transfer': - if action.get('src_mode') == '1': - try: - ssh.put_file(os.path.join(REPOS_DIR, env.SPUG_DEPLOY_ID, spug_version), action['dst']) - except Exception as e: - helper.send_error(host.id, f'exception: {e}') - helper.send_info(host.id, 'transfer completed\r\n') - continue - else: - sp_dir, sd_dst = os.path.split(action['src']) - tar_gz_file = f'{spug_version}.tar.gz' - try: - ssh.put_file(os.path.join(sp_dir, tar_gz_file), f'/tmp/{tar_gz_file}') - except Exception as e: - helper.send_error(host.id, f'exception: {e}') + with host.get_ssh(default_env=env) as ssh: + for index, action in enumerate(actions): + helper.send_step(h_id, 1 + index, f'{human_time()} {action["title"]}...\r\n') + if action.get('type') == 'transfer': + if action.get('src_mode') == '1': + try: + ssh.put_file(os.path.join(REPOS_DIR, env.SPUG_DEPLOY_ID, spug_version), action['dst']) + except Exception as e: + helper.send_error(host.id, f'exception: {e}') + helper.send_info(host.id, 'transfer completed\r\n') + continue + else: + sp_dir, sd_dst = os.path.split(action['src']) + tar_gz_file = f'{spug_version}.tar.gz' + try: + ssh.put_file(os.path.join(sp_dir, tar_gz_file), f'/tmp/{tar_gz_file}') + except Exception as e: + helper.send_error(host.id, f'exception: {e}') - command = f'mkdir -p /tmp/{spug_version} && tar xf /tmp/{tar_gz_file} -C /tmp/{spug_version}/ ' - command += f'&& rm -rf {action["dst"]} && mv /tmp/{spug_version}/{sd_dst} {action["dst"]} ' - command += f'&& rm -rf /tmp/{spug_version}* && echo "transfer completed"' - else: - command = f'cd /tmp ; {action["data"]}' - helper.remote(host.id, ssh, command, env) + command = f'mkdir -p /tmp/{spug_version} && tar xf /tmp/{tar_gz_file} -C /tmp/{spug_version}/ ' + command += f'&& rm -rf {action["dst"]} && mv /tmp/{spug_version}/{sd_dst} {action["dst"]} ' + command += f'&& rm -rf /tmp/{spug_version}* && echo "transfer completed"' + else: + command = f'cd /tmp ; {action["data"]}' + helper.remote(host.id, ssh, command) helper.send_step(h_id, 100, f'\r\n{human_time()} ** 发布成功 **') @@ -400,7 +400,6 @@ class Helper: if env: env = dict(env.items()) env.update(os.environ) - command = 'set -e\n' + command task = subprocess.Popen(command, env=env, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) while True: message = task.stdout.readline() @@ -416,3 +415,8 @@ class Helper: self.send_info(key, out) if code != 0: self.send_error(key, f'exit code: {code}') + + def remote_raw(self, key, ssh, command): + code, out = ssh.exec_command_raw(command) + if code != 0: + self.send_error(key, f'exit code: {code}') diff --git a/spug_api/apps/deploy/views.py b/spug_api/apps/deploy/views.py index e72d5f3..3b61e80 100644 --- a/spug_api/apps/deploy/views.py +++ b/spug_api/apps/deploy/views.py @@ -124,10 +124,10 @@ class RequestDetailView(View): if not req: return json_response(error='未找到指定发布申请') hosts = Host.objects.filter(id__in=json.loads(req.host_ids)) - outputs = {x.id: {'id': x.id, 'title': x.name, 'data': [f'{human_time()} 读取数据... ']} for x in hosts} + outputs = {x.id: {'id': x.id, 'title': x.name, 'data': f'{human_time()} 读取数据... '} for x in hosts} response = {'outputs': outputs, 'status': req.status} if req.deploy.extend == '2': - outputs['local'] = {'id': 'local', 'data': [f'{human_time()} 读取数据... ']} + outputs['local'] = {'id': 'local', 'data': f'{human_time()} 读取数据... '} response['s_actions'] = json.loads(req.deploy.extend_obj.server_actions) response['h_actions'] = json.loads(req.deploy.extend_obj.host_actions) if not response['h_actions']: @@ -139,7 +139,7 @@ class RequestDetailView(View): for item in data: item = json.loads(item.decode()) if 'data' in item: - outputs[item['key']]['data'].append(item['data']) + outputs[item['key']]['data'] += item['data'] if 'step' in item: outputs[item['key']]['step'] = item['step'] if 'status' in item: @@ -160,7 +160,7 @@ class RequestDetailView(View): return json_response(error='该申请单当前状态还不能执行发布') hosts = Host.objects.filter(id__in=json.loads(req.host_ids)) message = f'{human_time()} 等待调度... ' - outputs = {x.id: {'id': x.id, 'title': x.name, 'step': 0, 'data': [message]} for x in hosts} + outputs = {x.id: {'id': x.id, 'title': x.name, 'step': 0, 'data': message} for x in hosts} req.status = '2' req.do_at = human_datetime() req.do_by = request.user @@ -168,7 +168,7 @@ class RequestDetailView(View): Thread(target=dispatch, args=(req,)).start() if req.deploy.extend == '2': message = f'{human_time()} 建立连接... ' - outputs['local'] = {'id': 'local', 'step': 0, 'data': [message]} + outputs['local'] = {'id': 'local', 'step': 0, 'data': message} s_actions = json.loads(req.deploy.extend_obj.server_actions) h_actions = json.loads(req.deploy.extend_obj.host_actions) if not h_actions: diff --git a/spug_api/apps/exec/executors.py b/spug_api/apps/exec/executors.py index 2af5297..edaf3e9 100644 --- a/spug_api/apps/exec/executors.py +++ b/spug_api/apps/exec/executors.py @@ -39,7 +39,7 @@ class Job: def run(self): if not self.token: with self.ssh: - return self.ssh.exec_command_raw(self.command) + return self.ssh.exec_command(self.command) self.send('\x1b[36m### Executing ...\x1b[0m\r') code = -1 try: diff --git a/spug_api/apps/host/models.py b/spug_api/apps/host/models.py index 824e6e2..803d23b 100644 --- a/spug_api/apps/host/models.py +++ b/spug_api/apps/host/models.py @@ -24,9 +24,9 @@ class Host(models.Model, ModelMixin): def private_key(self): return self.pkey or AppSetting.get('private_key') - def get_ssh(self, pkey=None): + def get_ssh(self, pkey=None, default_env=None): pkey = pkey or self.private_key - return SSH(self.hostname, self.port, self.username, pkey) + return SSH(self.hostname, self.port, self.username, pkey, default_env=default_env) def to_view(self): tmp = self.to_dict() diff --git a/spug_api/apps/host/utils.py b/spug_api/apps/host/utils.py index b4c1eff..4597894 100644 --- a/spug_api/apps/host/utils.py +++ b/spug_api/apps/host/utils.py @@ -189,7 +189,8 @@ def fetch_host_extend(ssh): "fdisk -l 2> /dev/null | grep '^Disk /' | awk '{print $5}'", "fdisk -l 2> /dev/null | grep '^磁盘 /' | awk '{print $4}' | awk -F',' '{print $2}'" ] - code, out = ssh.exec_command(';'.join(commands)) + with ssh: + code, out = ssh.exec_command_raw(';'.join(commands)) if code != 0: raise Exception(out) response = {'disk': [], 'public_ip_address': [], 'private_ip_address': []} @@ -252,11 +253,11 @@ def _sync_host_extend(host, private_key=None, public_key=None, password=None, ss def _get_ssh(kwargs, pkey=None, private_key=None, public_key=None, password=None): try: ssh = SSH(pkey=pkey or private_key, **kwargs) - ssh.ping() + ssh.get_client() return ssh except AuthenticationException as e: if password: - ssh = SSH(password=str(password), **kwargs) - ssh.add_public_key(public_key) + with SSH(password=str(password), **kwargs) as ssh: + ssh.add_public_key(public_key) return _get_ssh(kwargs, private_key) raise e diff --git a/spug_api/apps/monitor/executors.py b/spug_api/apps/monitor/executors.py index e9172a8..a654613 100644 --- a/spug_api/apps/monitor/executors.py +++ b/spug_api/apps/monitor/executors.py @@ -56,8 +56,8 @@ def ping_check(addr): def host_executor(host, command): try: - cli = host.get_ssh() - exit_code, out = cli.exec_command(command) + with host.get_ssh() as ssh: + exit_code, out = ssh.exec_command(command) if exit_code == 0: return True, out or '检测状态正常' else: diff --git a/spug_api/apps/schedule/executors.py b/spug_api/apps/schedule/executors.py index 4a00efb..533f0b5 100644 --- a/spug_api/apps/schedule/executors.py +++ b/spug_api/apps/schedule/executors.py @@ -28,8 +28,8 @@ def local_executor(command): def host_executor(host, command): code, out, now = 1, None, time.time() try: - cli = host.get_ssh() - code, out = cli.exec_command(command) + with host.get_ssh() as ssh: + code, out = ssh.exec_command(command) except AuthenticationException: out = 'ssh authentication fail' except socket.error as e: diff --git a/spug_api/libs/ssh.py b/spug_api/libs/ssh.py index 9f4d013..74deb4f 100644 --- a/spug_api/libs/ssh.py +++ b/spug_api/libs/ssh.py @@ -10,13 +10,15 @@ import re class SSH: - def __init__(self, hostname, port=22, username='root', pkey=None, password=None, connect_timeout=10): + def __init__(self, hostname, port=22, username='root', pkey=None, password=None, default_env=None, + connect_timeout=10): self.stdout = None self.client = None self.channel = None self.sftp = None self.eof = 'Spug EOF 2108111926' - self.regex = re.compile(r'Spug EOF 2108111926 \d+[\r\n]?$') + self.default_env = self._make_env_command(default_env) + self.regex = re.compile(r'Spug EOF 2108111926 -?\d+[\r\n]?$') self.arguments = { 'hostname': hostname, 'port': port, @@ -36,22 +38,20 @@ class SSH: def get_client(self): if self.client is not None: return self.client - print('\n~~ ssh start ~~') self.client = SSHClient() self.client.set_missing_host_key_policy(AutoAddPolicy) self.client.connect(**self.arguments) return self.client def ping(self): - self.get_client() return True def add_public_key(self, public_key): command = f'mkdir -p -m 700 ~/.ssh && \ echo {public_key!r} >> ~/.ssh/authorized_keys && \ chmod 600 ~/.ssh/authorized_keys' - _, out, _ = self.client.exec_command(command) - if out.channel.recv_exit_status() != 0: + exit_code, out = self.exec_command_raw(command) + if exit_code != 0: raise Exception(f'add public key error: {out}') def exec_command_raw(self, command): @@ -69,7 +69,7 @@ class SSH: channel.send(command) out, exit_code = '', -1 for line in self.stdout: - if line.startswith(self.eof): + if self.regex.search(line): exit_code = int(line.rsplit()[-1]) break out += line @@ -91,10 +91,6 @@ class SSH: yield exit_code, line yield exit_code, line - def get_file(self, file): - sftp = self._get_sftp() - return sftp.open(file) - def put_file(self, local_path, remote_path): sftp = self._get_sftp() sftp.put(local_path, remote_path) @@ -115,13 +111,17 @@ class SSH: if self.channel: return self.channel - counter, data = 0, '' + counter = 0 self.channel = self.client.invoke_shell() - self.channel.send(b'export PS1= && stty -echo && echo Spug execute start\n') + command = 'export PS1= && stty -echo' + if self.default_env: + command += f' && {self.default_env}' + command += f' && echo {self.eof} $?\n' + self.channel.send(command.encode()) while True: if self.channel.recv_ready(): - data += self.channel.recv(8196).decode() - if 'Spug execute start\r\n' in data: + line = self.channel.recv(8196).decode() + if self.regex.search(line): self.stdout = self.channel.makefile('r') break elif counter >= 100: @@ -168,6 +168,5 @@ class SSH: return self def __exit__(self, exc_type, exc_val, exc_tb): - print('close √') self.client.close() self.client = None diff --git a/spug_web/src/pages/deploy/request/Ext1Console.js b/spug_web/src/pages/deploy/request/Ext1Console.js index f3c6885..7228fdd 100644 --- a/spug_web/src/pages/deploy/request/Ext1Console.js +++ b/spug_web/src/pages/deploy/request/Ext1Console.js @@ -3,9 +3,9 @@ * Copyright (c) * Released under the AGPL-3.0 License. */ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { observer, useLocalStore } from 'mobx-react'; -import { Card, Progress, Modal, Collapse, Steps } from 'antd'; +import { Card, Progress, Modal, Collapse, Steps, Skeleton } from 'antd'; import { ShrinkOutlined, CaretRightOutlined, LoadingOutlined, CloseOutlined } from '@ant-design/icons'; import OutView from './OutView'; import { http, X_TOKEN } from 'libs'; @@ -14,6 +14,8 @@ import store from './store'; function Ext1Console(props) { const outputs = useLocalStore(() => ({})); + const terms = useLocalStore(() => ({})); + const [fetching, setFetching] = useState(true); useEffect(props.request.mode === 'read' ? readDeploy : doDeploy, []) @@ -22,6 +24,7 @@ function Ext1Console(props) { http.get(`/api/deploy/request/${props.request.id}/`) .then(res => { Object.assign(outputs, res.outputs) + setTimeout(() => setFetching(false), 100) if (res.status === '2') { socket = _makeSocket() } @@ -34,6 +37,7 @@ function Ext1Console(props) { http.post(`/api/deploy/request/${props.request.id}/`) .then(res => { Object.assign(outputs, res.outputs) + setTimeout(() => setFetching(false), 100) socket = _makeSocket() store.fetchRecords() }) @@ -52,7 +56,10 @@ function Ext1Console(props) { } else { index += 1; const {key, data, step, status} = JSON.parse(e.data); - if (data !== undefined) outputs[key].data.push(data); + if (data !== undefined) { + outputs[key].data += data + if (terms[key]) terms[key].write(data) + } if (step !== undefined) outputs[key].step = step; if (status !== undefined) outputs[key].status = status; } @@ -73,6 +80,13 @@ function Ext1Console(props) { store.tabModes[props.request.id] = !value } + function handleSetTerm(term, key) { + if (outputs[key] && outputs[key].data) { + term.write(outputs[key].data) + } + terms[key] = term + } + return store.tabModes[props.request.id] ? ( ]}> - }> - {Object.values(outputs).map((item, index) => ( - - {item.title} - - - - - - - - }> - - - ))} - - + + }> + {Object.entries(outputs).map(([key, item], index) => ( + + {item.title} + + + + + + + + }> + handleSetTerm(term, key)}/> + + ))} + + ) } diff --git a/spug_web/src/pages/deploy/request/Ext2Console.js b/spug_web/src/pages/deploy/request/Ext2Console.js index aa9d527..8347672 100644 --- a/spug_web/src/pages/deploy/request/Ext2Console.js +++ b/spug_web/src/pages/deploy/request/Ext2Console.js @@ -5,7 +5,7 @@ */ import React, { useEffect, useState } from 'react'; import { observer, useLocalStore } from 'mobx-react'; -import { Card, Progress, Modal, Collapse, Steps } from 'antd'; +import { Card, Progress, Modal, Collapse, Steps, Skeleton } from 'antd'; import { ShrinkOutlined, CaretRightOutlined, LoadingOutlined, CloseOutlined } from '@ant-design/icons'; import OutView from './OutView'; import { http, X_TOKEN } from 'libs'; @@ -13,9 +13,11 @@ import styles from './index.module.less'; import store from './store'; function Ext2Console(props) { + const terms = useLocalStore(() => ({})); const outputs = useLocalStore(() => ({local: {id: 'local'}})); const [sActions, setSActions] = useState([]); const [hActions, setHActions] = useState([]); + const [fetching, setFetching] = useState(true); useEffect(props.request.mode === 'read' ? readDeploy : doDeploy, []) @@ -25,7 +27,8 @@ function Ext2Console(props) { .then(res => { setSActions(res.s_actions); setHActions(res.h_actions); - Object.assign(outputs, res.outputs) + Object.assign(outputs, res.outputs); + setTimeout(() => setFetching(false), 100) if (res.status === '2') { socket = _makeSocket() } @@ -40,6 +43,7 @@ function Ext2Console(props) { setSActions(res.s_actions); setHActions(res.h_actions); Object.assign(outputs, res.outputs) + setTimeout(() => setFetching(false), 100) socket = _makeSocket() }) return () => socket && socket.close() @@ -57,7 +61,10 @@ function Ext2Console(props) { } else { index += 1; const {key, data, step, status} = JSON.parse(e.data); - if (data !== undefined) outputs[key].data.push(data); + if (data !== undefined) { + outputs[key].data += data + if (terms[key]) terms[key].write(data) + } if (step !== undefined) outputs[key].step = step; if (status !== undefined) outputs[key].status = status; } @@ -80,6 +87,13 @@ function Ext2Console(props) { store.tabModes[props.request.id] = !value } + function handleSetTerm(term, key) { + if (outputs[key] && outputs[key].data) { + term.write(outputs[key].data) + } + terms[key] = term + } + const hostOutputs = Object.values(outputs).filter(x => x.id !== 'local'); return store.tabModes[props.request.id] ? ( ]}> - - - - - - {sActions.map((item, index) => ( - - ))} - - - )}> - - - - {hostOutputs.length > 0 && ( - }> - {hostOutputs.map((item, index) => ( - - {item.title} - - - {hActions.map((action, index) => ( - - ))} - - }> - - - ))} + + + + + + + {sActions.map((item, index) => ( + + ))} + + + )}> + handleSetTerm(term, 'local')}/> + - )} + {hostOutputs.length > 0 && ( + }> + {hostOutputs.map((item, index) => ( + + {item.title} + + + {hActions.map((action, index) => ( + + ))} + + }> + handleSetTerm(term, item.id)}/> + + ))} + + )} + ) } diff --git a/spug_web/src/pages/deploy/request/OutView.js b/spug_web/src/pages/deploy/request/OutView.js index 8213f80..3f384a7 100644 --- a/spug_web/src/pages/deploy/request/OutView.js +++ b/spug_web/src/pages/deploy/request/OutView.js @@ -3,18 +3,28 @@ * Copyright (c) * Released under the AGPL-3.0 License. */ -import React, { useRef, useEffect } from 'react'; -import styles from './index.module.less'; +import React, { useEffect, useRef } from 'react'; +import { FitAddon } from 'xterm-addon-fit'; +import { Terminal } from 'xterm'; function OutView(props) { const el = useRef() useEffect(() => { - if (el) el.current.scrollTop = el.current.scrollHeight - }) + const fitPlugin = new FitAddon() + const term = new Terminal({disableStdin: true}) + term.loadAddon(fitPlugin) + term.setOption('theme', {background: '#fff', foreground: '#000', selection: '#999'}) + term.open(el.current) + props.setTerm(term) + fitPlugin.fit() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) return ( -
{props.records}
+
+
+
) } diff --git a/spug_web/src/pages/deploy/request/index.module.less b/spug_web/src/pages/deploy/request/index.module.less index 3f8ae9a..bd1a5b7 100644 --- a/spug_web/src/pages/deploy/request/index.module.less +++ b/spug_web/src/pages/deploy/request/index.module.less @@ -92,12 +92,6 @@ color: #1890ff; } } - - .out { - min-height: 40px; - max-height: 400px; - padding: 10px 15px; - } } .collapse :global(.ant-collapse-content-box) { diff --git a/spug_web/src/pages/exec/task/OutView.js b/spug_web/src/pages/exec/task/OutView.js index 061eb87..17c2c39 100644 --- a/spug_web/src/pages/exec/task/OutView.js +++ b/spug_web/src/pages/exec/task/OutView.js @@ -18,17 +18,16 @@ function OutView(props) { term.open(el.current) const data = props.getOutput() if (data) term.write(data) - term.fit = () => { - const dimensions = fitPlugin.proposeDimensions() - if (dimensions.cols && dimensions.rows) fitPlugin.fit() - } + term.fit = () => fitPlugin.fit() props.setTerm(term) fitPlugin.fit() // eslint-disable-next-line react-hooks/exhaustive-deps }, []) return ( -
+
+
+
) }