mirror of https://github.com/openspug/spug
A 自定义发布支持发布时上传数据 #156
parent
708ed4119c
commit
c82906a5df
|
@ -7,5 +7,6 @@ from .views import *
|
|||
|
||||
urlpatterns = [
|
||||
path('request/', RequestView.as_view()),
|
||||
path('request/upload/', do_upload),
|
||||
path('request/<int:r_id>/', RequestDetailView.as_view()),
|
||||
]
|
||||
|
|
|
@ -136,7 +136,9 @@ def _ext2_deploy(req, helper, env):
|
|||
tmp_transfer_file = None
|
||||
for action in host_actions:
|
||||
if action.get('type') == 'transfer':
|
||||
helper.send_info('local', f'{human_time()} 检测到数据传输动作,执行打包... ')
|
||||
if action.get('src_mode') == '1':
|
||||
break
|
||||
helper.send_info('local', f'{human_time()} 检测到来源为本地路径的数据传输动作,执行打包... ')
|
||||
action['src'] = action['src'].rstrip('/ ')
|
||||
action['dst'] = action['dst'].rstrip('/ ')
|
||||
if not action['src'] or not action['dst']:
|
||||
|
@ -242,6 +244,14 @@ def _deploy_ext2_host(helper, h_id, actions, env):
|
|||
for index, action in enumerate(actions):
|
||||
helper.send_step(h_id, 2 + 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, env.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'{env.SPUG_VERSION}.tar.gz'
|
||||
try:
|
||||
|
|
|
@ -4,17 +4,20 @@
|
|||
from django.views.generic import View
|
||||
from django.db.models import F
|
||||
from django.conf import settings
|
||||
from django.http.response import HttpResponseBadRequest
|
||||
from django_redis import get_redis_connection
|
||||
from libs import json_response, JsonParser, Argument, human_datetime, human_time
|
||||
from apps.deploy.models import DeployRequest
|
||||
from apps.app.models import Deploy
|
||||
from apps.app.models import Deploy, DeployExtend2
|
||||
from apps.deploy.utils import deploy_dispatch, Helper
|
||||
from apps.host.models import Host
|
||||
from collections import defaultdict
|
||||
from threading import Thread
|
||||
from datetime import datetime
|
||||
import subprocess
|
||||
import json
|
||||
import uuid
|
||||
import os
|
||||
|
||||
|
||||
class RequestView(View):
|
||||
|
@ -63,6 +66,11 @@ class RequestView(View):
|
|||
return json_response(error='请选择要发布的Tag')
|
||||
if form.extra[0] == 'branch' and not form.extra[2]:
|
||||
return json_response(error='请选择要发布的分支及Commit ID')
|
||||
if deploy.extend == '2':
|
||||
if DeployExtend2.objects.filter(host_actions__contains='"src_mode": "1"').exists():
|
||||
if len(form.extra) < 2:
|
||||
return json_response(error='该应用的发布配置中使用了数据传输动作且设置为发布时上传,请上传要传输的数据')
|
||||
form.version = form.extra[1].get('path')
|
||||
form.status = '0' if deploy.is_audit else '1'
|
||||
form.extra = json.dumps(form.extra)
|
||||
form.host_ids = json.dumps(form.host_ids)
|
||||
|
@ -215,3 +223,22 @@ class RequestDetailView(View):
|
|||
req.save()
|
||||
Thread(target=Helper.send_deploy_notify, args=(req, 'approve_rst')).start()
|
||||
return json_response(error=error)
|
||||
|
||||
|
||||
def do_upload(request):
|
||||
repos_dir = settings.REPOS_DIR
|
||||
file = request.FILES['file']
|
||||
deploy_id = request.POST.get('deploy_id')
|
||||
if file and deploy_id:
|
||||
dir_name = os.path.join(repos_dir, deploy_id)
|
||||
file_name = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||
command = f'mkdir -p {dir_name} && cd {dir_name} && ls | sort -rn | tail -n +11 | xargs rm -rf'
|
||||
code, outputs = subprocess.getstatusoutput(command)
|
||||
if code != 0:
|
||||
return json_response(error=outputs)
|
||||
with open(os.path.join(dir_name, file_name), 'wb') as f:
|
||||
for chunk in file.chunks():
|
||||
f.write(chunk)
|
||||
return json_response(file_name)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
|
|
@ -33,7 +33,7 @@ class Ext2Setup3 extends React.Component {
|
|||
const info = store.deploy;
|
||||
info['app_id'] = store.app_id;
|
||||
info['extend'] = '2';
|
||||
info['host_actions'] = info['host_actions'].filter(x => (x.title && x.data) || (x.title && x.src && x.dst));
|
||||
info['host_actions'] = info['host_actions'].filter(x => (x.title && x.data) || (x.title && (x.src || x.src_mode === '1') && x.dst));
|
||||
info['server_actions'] = info['server_actions'].filter(x => x.title && x.data);
|
||||
http.post('/api/app/deploy/', info)
|
||||
.then(res => {
|
||||
|
@ -101,7 +101,23 @@ class Ext2Setup3 extends React.Component {
|
|||
placeholder="请输入"/>
|
||||
</Form.Item>
|
||||
{item['type'] === 'transfer' ? ([
|
||||
<Form.Item key={0} label="过滤规则" help={this.helpMap[item['mode']]}>
|
||||
<Form.Item key={0} required label="数据来源">
|
||||
<Input
|
||||
spellCheck={false}
|
||||
disabled={store.isReadOnly || item['src_mode'] === '1'}
|
||||
placeholder="请输入本地(部署spug的容器或主机)路径"
|
||||
value={item['src']}
|
||||
onChange={e => item['src'] = e.target.value}
|
||||
addonBefore={(
|
||||
<Select disabled={store.isReadOnly} style={{width: 120}} value={item['src_mode'] || '0'}
|
||||
onChange={v => item['src_mode'] = v}>
|
||||
<Select.Option value="0">本地路径</Select.Option>
|
||||
<Select.Option value="1">发布时上传</Select.Option>
|
||||
</Select>
|
||||
)}/>
|
||||
</Form.Item>,
|
||||
item['src_mode'] === '0' ? (
|
||||
<Form.Item key={1} label="过滤规则" help={this.helpMap[item['mode']]}>
|
||||
<Input
|
||||
spellCheck={false}
|
||||
placeholder="请输入逗号分割的过滤规则"
|
||||
|
@ -109,23 +125,18 @@ class Ext2Setup3 extends React.Component {
|
|||
onChange={e => item['rule'] = e.target.value.replace(',', ',')}
|
||||
disabled={store.isReadOnly || item['mode'] === '0'}
|
||||
addonBefore={(
|
||||
<Select disabled={store.isReadOnly} style={{width: 100}} value={item['mode']}
|
||||
<Select disabled={store.isReadOnly} style={{width: 120}} value={item['mode']}
|
||||
onChange={v => item['mode'] = v}>
|
||||
<Select.Option value="0">关闭</Select.Option>
|
||||
<Select.Option value="1">包含</Select.Option>
|
||||
<Select.Option value="2">排除</Select.Option>
|
||||
</Select>
|
||||
)}/>
|
||||
</Form.Item>,
|
||||
<Form.Item key={1} required label="传输路径" extra={<a
|
||||
</Form.Item>
|
||||
) : null,
|
||||
<Form.Item key={2} required label="目标路径" extra={<a
|
||||
target="_blank" rel="noopener noreferrer"
|
||||
href="https://spug.dev/docs/deploy-config#%E6%95%B0%E6%8D%AE%E4%BC%A0%E8%BE%93">使用前请务必阅读官方文档。</a>}>
|
||||
<Input
|
||||
disabled={store.isReadOnly}
|
||||
spellCheck={false}
|
||||
value={item['src']}
|
||||
placeholder="请输入本地路径(部署spug的容器或主机)"
|
||||
onChange={e => item['src'] = e.target.value}/>
|
||||
<Input
|
||||
disabled={store.isReadOnly}
|
||||
spellCheck={false}
|
||||
|
@ -162,7 +173,7 @@ class Ext2Setup3 extends React.Component {
|
|||
block
|
||||
type="dashed"
|
||||
disabled={store.isReadOnly || lds.findIndex(host_actions, x => x.type === 'transfer') !== -1}
|
||||
onClick={() => host_actions.push({type: 'transfer', title: '数据传输', mode: '0'})}>
|
||||
onClick={() => host_actions.push({type: 'transfer', title: '数据传输', mode: '0', src_mode: '0'})}>
|
||||
<Icon type="plus"/>添加数据传输动作(仅能添加一个)
|
||||
</Button>
|
||||
</Form.Item>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Modal, Form, Input, Tag, message } from 'antd';
|
||||
import { Modal, Form, Input, Tag, Upload, message, Button, Icon } from 'antd';
|
||||
import hostStore from 'pages/host/store';
|
||||
import http from 'libs/http';
|
||||
import store from './store';
|
||||
|
@ -15,9 +15,11 @@ import lds from 'lodash';
|
|||
class Ext2Form extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.token = localStorage.getItem('token');
|
||||
this.state = {
|
||||
loading: false,
|
||||
type: null,
|
||||
uploading: false,
|
||||
fileList: [],
|
||||
host_ids: store.record['app_host_ids'].concat()
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +28,11 @@ class Ext2Form extends React.Component {
|
|||
if (hostStore.records.length === 0) {
|
||||
hostStore.fetchRecords()
|
||||
}
|
||||
const file = lds.get(store, 'record.extra.1');
|
||||
if (file) {
|
||||
file.uid = '0';
|
||||
this.setState({fileList: [file]})
|
||||
}
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
|
@ -37,6 +44,9 @@ class Ext2Form extends React.Component {
|
|||
formData['id'] = store.record.id;
|
||||
formData['deploy_id'] = store.record.deploy_id;
|
||||
formData['extra'] = [formData['extra']];
|
||||
if (this.state.fileList.length > 0) {
|
||||
formData['extra'].push(lds.pick(this.state.fileList[0], ['path', 'name']))
|
||||
}
|
||||
formData['host_ids'] = this.state.host_ids;
|
||||
http.post('/api/deploy/request/', formData)
|
||||
.then(res => {
|
||||
|
@ -57,9 +67,28 @@ class Ext2Form extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
handleUploadChange = (v) => {
|
||||
if (v.fileList.length === 0) {
|
||||
this.setState({fileList: []})
|
||||
} else {
|
||||
this.setState({fileList: [v.file]})
|
||||
}
|
||||
};
|
||||
|
||||
handleUpload = (file, fileList) => {
|
||||
this.setState({uploading: true});
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('deploy_id', store.record.deploy_id);
|
||||
http.post('/api/deploy/request/upload/', formData)
|
||||
.then(res => file.path = res)
|
||||
.finally(() => this.setState({uploading: false}))
|
||||
return false
|
||||
};
|
||||
|
||||
render() {
|
||||
const info = store.record;
|
||||
const {host_ids} = this.state;
|
||||
const {host_ids, fileList, uploading} = this.state;
|
||||
const {getFieldDecorator} = this.props.form;
|
||||
return (
|
||||
<Modal
|
||||
|
@ -81,10 +110,11 @@ class Ext2Form extends React.Component {
|
|||
<Input placeholder="请输入环境变量 SPUG_RELEASE 的值"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="备注信息">
|
||||
{getFieldDecorator('desc', {initialValue: info['desc']})(
|
||||
<Input placeholder="请输入备注信息"/>
|
||||
)}
|
||||
<Form.Item label="上传数据" help="通过数据传输动作来使用上传的文件。">
|
||||
<Upload name="file" fileList={fileList} headers={{'X-Token': this.token}} beforeUpload={this.handleUpload}
|
||||
data={{deploy_id: info.deploy_id}} onChange={this.handleUploadChange}>
|
||||
{fileList.length === 0 ? <Button loading={uploading}><Icon type="upload"/> 点击上传</Button> : null}
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
<Form.Item required label="发布目标主机" help="通过点击主机名称自由选择本次发布的主机。">
|
||||
{info['app_host_ids'].map(id => (
|
||||
|
|
Loading…
Reference in New Issue