From 1897739512529d8dc976ff175927d0e2ad390f10 Mon Sep 17 00:00:00 2001 From: vapao Date: Tue, 17 Aug 2021 16:28:02 +0800 Subject: [PATCH] fix issue --- spug_api/apps/deploy/utils.py | 4 +- spug_api/apps/repository/urls.py | 1 + spug_api/apps/repository/views.py | 32 ++++++++++++- spug_api/libs/utils.py | 5 +- .../src/pages/deploy/repository/Console.js | 46 +++++++++++++------ 5 files changed, 71 insertions(+), 17 deletions(-) diff --git a/spug_api/apps/deploy/utils.py b/spug_api/apps/deploy/utils.py index 0904565..fe43531 100644 --- a/spug_api/apps/deploy/utils.py +++ b/spug_api/apps/deploy/utils.py @@ -165,7 +165,7 @@ def _deploy_ext1_host(req, helper, h_id, env): 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}') + helper.remote_raw(host.id, ssh, f'cd {extend.dst_repo} && {clean_command}') # transfer files tar_gz_file = f'{req.spug_version}.tar.gz' try: @@ -173,7 +173,7 @@ def _deploy_ext1_host(req, helper, h_id, env): 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' + command = f'cd {extend.dst_repo} && rm -rf {req.spug_version} && 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') diff --git a/spug_api/apps/repository/urls.py b/spug_api/apps/repository/urls.py index 6132a1f..cf0a221 100644 --- a/spug_api/apps/repository/urls.py +++ b/spug_api/apps/repository/urls.py @@ -7,5 +7,6 @@ from .views import * urlpatterns = [ path('', RepositoryView.as_view()), + path('/', get_detail), path('request/', get_requests), ] diff --git a/spug_api/apps/repository/views.py b/spug_api/apps/repository/views.py index bf7f12d..bbf0b2f 100644 --- a/spug_api/apps/repository/views.py +++ b/spug_api/apps/repository/views.py @@ -4,7 +4,8 @@ from django.views.generic import View from django.db.models import F from django.conf import settings -from libs import json_response, JsonParser, Argument, AttrDict +from django_redis import get_redis_connection +from libs import json_response, JsonParser, Argument, human_time, AttrDict from apps.repository.models import Repository from apps.deploy.models import DeployRequest from apps.repository.utils import dispatch @@ -102,3 +103,32 @@ def get_requests(request): data['status_alias'] = item.get_status_display() requests.append(data) return json_response(requests) + + +def get_detail(request, r_id): + repository = Repository.objects.filter(pk=r_id).first() + if not repository: + return json_response(error='未找到指定构建记录') + rds, counter = get_redis_connection(), 0 + key = f'{settings.BUILD_KEY}:{repository.spug_version}' + data = rds.lrange(key, counter, counter + 9) + response = AttrDict(data='', step=0, s_status='process', status=repository.status) + while data: + for item in data: + counter += 1 + item = json.loads(item.decode()) + if 'data' in item: + response.data += item['data'] + if 'step' in item: + response.step = item['step'] + if 'status' in item: + response.status = item['status'] + data = rds.lrange(key, counter, counter + 9) + response.index = counter + if repository.status in ('0', '1'): + response.data = f'{human_time()} 建立连接... ' + response.data + elif not response.data: + response.data = f'{human_time()} 读取数据... \r\n\r\n未读取到数据,Spug 仅保存最近2周的构建日志。' + else: + response.data = f'{human_time()} 读取数据... ' + response.data + return json_response(response) diff --git a/spug_api/libs/utils.py b/spug_api/libs/utils.py index dd0b4af..f1f2046 100644 --- a/spug_api/libs/utils.py +++ b/spug_api/libs/utils.py @@ -79,7 +79,10 @@ class AttrDict(dict): self.__setitem__(key, value) def __getattr__(self, item): - return self.__getitem__(item) + try: + return self.__getitem__(item) + except KeyError: + raise AttributeError(item) def __delattr__(self, item): self.__delitem__(item) diff --git a/spug_web/src/pages/deploy/repository/Console.js b/spug_web/src/pages/deploy/repository/Console.js index 906b94d..eeb0119 100644 --- a/spug_web/src/pages/deploy/repository/Console.js +++ b/spug_web/src/pages/deploy/repository/Console.js @@ -8,21 +8,35 @@ import { observer } from 'mobx-react'; import { FullscreenOutlined, FullscreenExitOutlined, LoadingOutlined } from '@ant-design/icons'; import { FitAddon } from 'xterm-addon-fit'; import { Terminal } from 'xterm'; -import { Modal, Steps } from 'antd'; -import { X_TOKEN, human_time } from 'libs'; +import { Modal, Steps, Spin } from 'antd'; +import { X_TOKEN, http } from 'libs'; import styles from './index.module.less'; import store from './store'; export default observer(function Console() { const el = useRef() + const [term] = useState(new Terminal({disableStdin: true})) const [fullscreen, setFullscreen] = useState(false); const [step, setStep] = useState(0); - const [status, setStatus] = useState('process') + const [status, setStatus] = useState('process'); + const [fetching, setFetching] = useState(true); useEffect(() => { - const term = initialTerm() - term.write(`${human_time()} 建立连接... `) - let index = 0; + let socket; + initialTerm() + http.get(`/api/repository/${store.record.id}/`) + .then(res => { + term.write(res.data) + setStep(res.step) + setStatus(res.status) + socket = _makeSocket(res.index) + }) + .finally(() => setFetching(false)) + return () => socket && socket.close() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + function _makeSocket(index = 0) { const token = store.record.spug_version; const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const socket = new WebSocket(`${protocol}//${window.location.host}/api/ws/build/${token}/?x-token=${X_TOKEN}`); @@ -38,17 +52,21 @@ export default observer(function Console() { if (status !== undefined) setStatus(status); } } - return () => socket.close(); - }, []) + return socket + } + + useEffect(() => { + term.fit && term.fit() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [fullscreen]) function initialTerm() { const fitPlugin = new FitAddon() - const term = new Terminal({disableStdin: true}) term.loadAddon(fitPlugin) term.setOption('theme', {background: '#fafafa', foreground: '#000', selection: '#999'}) term.open(el.current) + term.fit = () => fitPlugin.fit() fitPlugin.fit() - return term } function handleClose() { @@ -85,9 +103,11 @@ export default observer(function Console() { -
-
-
+ +
+
+
+ ) })