mirror of https://github.com/openspug/spug
fix issue
parent
1897739512
commit
0dd2ef3df8
|
@ -202,30 +202,45 @@ class RequestDetailView(View):
|
||||||
def post_request_ext1(request):
|
def post_request_ext1(request):
|
||||||
form, error = JsonParser(
|
form, error = JsonParser(
|
||||||
Argument('id', type=int, required=False),
|
Argument('id', type=int, required=False),
|
||||||
|
Argument('deploy_id', type=int, help='参数错误'),
|
||||||
Argument('name', help='请输入申请标题'),
|
Argument('name', help='请输入申请标题'),
|
||||||
Argument('repository_id', type=int, help='请选择发布版本'),
|
Argument('extra', type=list, help='请选择发布版本'),
|
||||||
Argument('host_ids', type=list, filter=lambda x: len(x), help='请选择要部署的主机'),
|
Argument('host_ids', type=list, filter=lambda x: len(x), help='请选择要部署的主机'),
|
||||||
Argument('type', default='1'),
|
Argument('type', default='1'),
|
||||||
Argument('plan', required=False),
|
Argument('plan', required=False),
|
||||||
Argument('desc', required=False),
|
Argument('desc', required=False),
|
||||||
).parse(request.body)
|
).parse(request.body)
|
||||||
if error is None:
|
if error is None:
|
||||||
repository = Repository.objects.filter(pk=form.repository_id).first()
|
deploy = Deploy.objects.get(pk=form.deploy_id)
|
||||||
if not repository:
|
form.spug_version = Repository.make_spug_version(deploy.id)
|
||||||
return json_response(error='未找到指定构建版本记录')
|
if form.extra[0] == 'tag':
|
||||||
form.name = form.name.replace("'", '')
|
if not form.extra[1]:
|
||||||
form.status = '0' if repository.deploy.is_audit else '1'
|
return json_response(error='请选择要发布的版本')
|
||||||
|
form.version = form.extra[1]
|
||||||
|
elif form.extra[0] == 'branch':
|
||||||
|
if not form.extra[2]:
|
||||||
|
return json_response(error='请选择要发布的分支及Commit ID')
|
||||||
|
form.version = f'{form.extra[1]}#{form.extra[2][:6]}'
|
||||||
|
elif form.extra[0] == 'repository':
|
||||||
|
if not form.extra[1]:
|
||||||
|
return json_response(error='请选择要发布的版本')
|
||||||
|
repository = Repository.objects.get(pk=form.extra[1])
|
||||||
|
form.repository_id = repository.id
|
||||||
form.version = repository.version
|
form.version = repository.version
|
||||||
form.spug_version = repository.spug_version
|
form.spug_version = repository.spug_version
|
||||||
form.deploy_id = repository.deploy_id
|
else:
|
||||||
|
return json_response(error='参数错误')
|
||||||
|
|
||||||
|
form.extra = json.dumps(form.extra)
|
||||||
|
form.status = '0' if deploy.is_audit else '1'
|
||||||
form.host_ids = json.dumps(sorted(form.host_ids))
|
form.host_ids = json.dumps(sorted(form.host_ids))
|
||||||
if form.id:
|
if form.id:
|
||||||
req = DeployRequest.objects.get(pk=form.id)
|
req = DeployRequest.objects.get(pk=form.id)
|
||||||
is_required_notify = repository.deploy.is_audit and req.status == '-1'
|
is_required_notify = deploy.is_audit and req.status == '-1'
|
||||||
DeployRequest.objects.filter(pk=form.id).update(created_by=request.user, reason=None, **form)
|
DeployRequest.objects.filter(pk=form.id).update(created_by=request.user, reason=None, **form)
|
||||||
else:
|
else:
|
||||||
req = DeployRequest.objects.create(created_by=request.user, **form)
|
req = DeployRequest.objects.create(created_by=request.user, **form)
|
||||||
is_required_notify = repository.deploy.is_audit
|
is_required_notify = deploy.is_audit
|
||||||
if is_required_notify:
|
if is_required_notify:
|
||||||
Thread(target=Helper.send_deploy_notify, args=(req, 'approve_req')).start()
|
Thread(target=Helper.send_deploy_notify, args=(req, 'approve_req')).start()
|
||||||
return json_response(error=error)
|
return json_response(error=error)
|
||||||
|
|
|
@ -42,6 +42,7 @@ export default observer(function () {
|
||||||
setExtra2(commit)
|
setExtra2(commit)
|
||||||
} else {
|
} else {
|
||||||
setExtra1(lds.get(Object.keys(tags), 0))
|
setExtra1(lds.get(Object.keys(tags), 0))
|
||||||
|
setExtra2(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { Modal, Form, Input, Select, DatePicker, Button, message } from 'antd';
|
import { Modal, Form, Input, Select, DatePicker, Button, message } from 'antd';
|
||||||
|
import { LoadingOutlined, SyncOutlined } from '@ant-design/icons';
|
||||||
import HostSelector from './HostSelector';
|
import HostSelector from './HostSelector';
|
||||||
import hostStore from 'pages/host/store';
|
import hostStore from 'pages/host/store';
|
||||||
import { http, history } from 'libs';
|
import { http, history } from 'libs';
|
||||||
|
@ -30,18 +31,38 @@ export default observer(function () {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [versions, setVersions] = useState([]);
|
const [repositories, setRepositories] = useState([]);
|
||||||
const [host_ids, setHostIds] = useState([]);
|
const [host_ids, setHostIds] = useState([]);
|
||||||
const [plan, setPlan] = useState(store.record.plan);
|
const [plan, setPlan] = useState(store.record.plan);
|
||||||
|
const [fetching, setFetching] = useState(false);
|
||||||
|
const [git_type, setGitType] = useState();
|
||||||
|
const [extra, setExtra] = useState([]);
|
||||||
|
const [extra1, setExtra1] = useState();
|
||||||
|
const [extra2, setExtra2] = useState();
|
||||||
|
const [versions, setVersions] = useState({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const {deploy_id, app_host_ids, host_ids} = store.record;
|
const {app_host_ids, host_ids} = store.record;
|
||||||
setHostIds(lds.clone(host_ids || app_host_ids));
|
setHostIds(lds.clone(host_ids || app_host_ids));
|
||||||
http.get('/api/repository/', {params: {deploy_id}})
|
|
||||||
.then(res => setVersions(res))
|
|
||||||
if (!hostStore.records || hostStore.records.length === 0) hostStore.fetchRecords()
|
if (!hostStore.records || hostStore.records.length === 0) hostStore.fetchRecords()
|
||||||
|
fetchVersions()
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
function fetchVersions() {
|
||||||
|
setFetching(true);
|
||||||
|
const deploy_id = store.record.deploy_id
|
||||||
|
const p1 = http.get(`/api/app/deploy/${deploy_id}/versions/`, {timeout: 120000})
|
||||||
|
const p2 = http.get('/api/repository/', {params: {deploy_id}})
|
||||||
|
Promise.all([p1, p2])
|
||||||
|
.then(([res1, res2]) => {
|
||||||
|
if (!versions.branches) _initial(res1, res2)
|
||||||
|
setVersions(res1)
|
||||||
|
setRepositories(res2)
|
||||||
|
})
|
||||||
|
.finally(() => setFetching(false))
|
||||||
|
}
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
if (host_ids.length === 0) {
|
if (host_ids.length === 0) {
|
||||||
return message.error('请至少选择一个要发布的主机')
|
return message.error('请至少选择一个要发布的主机')
|
||||||
|
@ -49,9 +70,10 @@ export default observer(function () {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const formData = form.getFieldsValue();
|
const formData = form.getFieldsValue();
|
||||||
formData['id'] = store.record.id;
|
formData['id'] = store.record.id;
|
||||||
|
formData['deploy_id'] = store.record.deploy_id;
|
||||||
formData['host_ids'] = host_ids;
|
formData['host_ids'] = host_ids;
|
||||||
formData['type'] = store.record.type;
|
formData['type'] = store.record.type;
|
||||||
formData['deploy_id'] = store.record.deploy_id;
|
formData['extra'] = [git_type, extra1, extra2];
|
||||||
if (plan) formData.plan = plan.format('YYYY-MM-DD HH:mm:00');
|
if (plan) formData.plan = plan.format('YYYY-MM-DD HH:mm:00');
|
||||||
http.post('/api/deploy/request/ext1/', formData)
|
http.post('/api/deploy/request/ext1/', formData)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
|
@ -61,32 +83,148 @@ export default observer(function () {
|
||||||
}, () => setLoading(false))
|
}, () => setLoading(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _setDefault(type, new_extra, new_versions, new_repositories) {
|
||||||
|
const now_extra = new_extra || extra;
|
||||||
|
const now_versions = new_versions || versions;
|
||||||
|
const now_repositories = new_repositories || repositories;
|
||||||
|
const {branches, tags} = now_versions;
|
||||||
|
if (type === 'branch') {
|
||||||
|
let [branch, commit] = [now_extra[1], null];
|
||||||
|
if (branches[branch]) {
|
||||||
|
commit = lds.get(branches[branch], '0.id')
|
||||||
|
} else {
|
||||||
|
branch = lds.get(Object.keys(branches), 0)
|
||||||
|
commit = lds.get(branches, [branch, 0, 'id'])
|
||||||
|
}
|
||||||
|
setExtra1(branch)
|
||||||
|
setExtra2(commit)
|
||||||
|
} else if (type === 'tag') {
|
||||||
|
setExtra1(lds.get(Object.keys(tags), 0))
|
||||||
|
setExtra2(null)
|
||||||
|
} else {
|
||||||
|
setExtra1(lds.get(now_repositories, '0.id'))
|
||||||
|
setExtra2(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _initial(versions, repositories) {
|
||||||
|
const {branches, tags} = versions;
|
||||||
|
if (branches && tags) {
|
||||||
|
for (let item of store.records) {
|
||||||
|
if (item.extra && item.deploy_id === store.record.deploy_id) {
|
||||||
|
const type = item.extra[0];
|
||||||
|
setExtra(item.extra);
|
||||||
|
setGitType(type);
|
||||||
|
return _setDefault(type, item.extra, versions, repositories);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setGitType('branch');
|
||||||
|
const branch = lds.get(Object.keys(branches), 0);
|
||||||
|
const commit = lds.get(branches, [branch, 0, 'id'])
|
||||||
|
setExtra1(branch);
|
||||||
|
setExtra2(commit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchType(v) {
|
||||||
|
setGitType(v);
|
||||||
|
_setDefault(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchExtra1(v) {
|
||||||
|
setExtra1(v)
|
||||||
|
if (git_type === 'branch') {
|
||||||
|
setExtra2(lds.get(versions.branches[v], '0.id'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const {app_host_ids, type, rb_id} = store.record;
|
const {app_host_ids, type, rb_id} = store.record;
|
||||||
|
const {branches, tags} = versions;
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
visible
|
visible
|
||||||
width={700}
|
width={800}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
title={`${store.record.id ? '编辑' : '新建'}发布申请`}
|
title={`${store.record.id ? '编辑' : '新建'}发布申请`}
|
||||||
onCancel={() => store.ext1Visible = false}
|
onCancel={() => store.ext1Visible = false}
|
||||||
confirmLoading={loading}
|
confirmLoading={loading}
|
||||||
onOk={handleSubmit}>
|
onOk={handleSubmit}>
|
||||||
<Form form={form} initialValues={store.record} labelCol={{span: 6}} wrapperCol={{span: 16}}>
|
<Form form={form} initialValues={store.record} labelCol={{span: 5}} wrapperCol={{span: 17}}>
|
||||||
<Form.Item required name="name" label="申请标题">
|
<Form.Item required name="name" label="申请标题">
|
||||||
<Input placeholder="请输入申请标题"/>
|
<Input placeholder="请输入申请标题"/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item required name="repository_id" label={type === '2' ? '回滚版本' : '发布版本'}>
|
<Form.Item required label="选择分支/标签/版本" style={{marginBottom: 12}} extra={<span>
|
||||||
<Select placeholder="请选择" notFoundContent={<NoVersions/>}>
|
根据网络情况,首次刷新可能会很慢,请耐心等待。
|
||||||
{versions.map(item => (
|
<a target="_blank" rel="noopener noreferrer"
|
||||||
|
href="https://spug.cc/docs/install-error/#%E6%96%B0%E5%BB%BA%E5%B8%B8%E8%A7%84%E5%8F%91%E5%B8%83%E7%94%B3%E8%AF%B7-git-clone-%E9%94%99%E8%AF%AF">clone 失败?</a>
|
||||||
|
</span>}>
|
||||||
|
<Form.Item style={{display: 'inline-block', marginBottom: 0, width: '450px'}}>
|
||||||
|
<Input.Group compact>
|
||||||
|
<Select value={git_type} onChange={switchType} style={{width: 100}}>
|
||||||
|
<Select.Option value="branch">Branch</Select.Option>
|
||||||
|
<Select.Option value="tag">Tag</Select.Option>
|
||||||
|
<Select.Option value="repository">构建仓库</Select.Option>
|
||||||
|
</Select>
|
||||||
|
<Select
|
||||||
|
showSearch
|
||||||
|
style={{width: 350}}
|
||||||
|
value={extra1}
|
||||||
|
placeholder="请稍等"
|
||||||
|
onChange={switchExtra1}
|
||||||
|
notFoundContent={git_type === 'repository' ? <NoVersions/> : undefined}
|
||||||
|
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}>
|
||||||
|
{git_type === 'branch' ? (
|
||||||
|
Object.keys(branches || {}).map(b => <Select.Option key={b} value={b}>{b}</Select.Option>)
|
||||||
|
) : git_type === 'tag' ? (
|
||||||
|
Object.entries(tags || {}).map(([tag, info]) => (
|
||||||
|
<Select.Option key={tag} value={tag}>
|
||||||
|
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
||||||
|
<span style={{
|
||||||
|
width: 200,
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis'
|
||||||
|
}}>{`${tag} ${info.author} ${info.message}`}</span>
|
||||||
|
<span style={{color: '#999', fontSize: 12}}>{info['date']} </span>
|
||||||
|
</div>
|
||||||
|
</Select.Option>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
repositories.map(item => (
|
||||||
<Select.Option key={item.id} value={item.id} disabled={type === '2' && item.id >= rb_id}>
|
<Select.Option key={item.id} value={item.id} disabled={type === '2' && item.id >= rb_id}>
|
||||||
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
||||||
<span>{item.remarks ? `${item.version} (${item.remarks})` : item.version}</span>
|
<span>{item.remarks ? `${item.version} (${item.remarks})` : item.version}</span>
|
||||||
<span style={{color: '#999', fontSize: 12}}>构建于 {moment(item.created_at).fromNow()}</span>
|
<span style={{color: '#999', fontSize: 12}}>构建于 {moment(item.created_at).fromNow()}</span>
|
||||||
</div>
|
</div>
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
))}
|
))
|
||||||
|
)}
|
||||||
|
</Select>
|
||||||
|
</Input.Group>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item style={{display: 'inline-block', width: 82, textAlign: 'center', marginBottom: 0}}>
|
||||||
|
{fetching ? <LoadingOutlined style={{fontSize: 18, color: '#1890ff'}}/> :
|
||||||
|
<Button type="link" icon={<SyncOutlined/>} disabled={fetching} onClick={fetchVersions}>刷新</Button>
|
||||||
|
}
|
||||||
|
</Form.Item>
|
||||||
|
</Form.Item>
|
||||||
|
{git_type === 'branch' && (
|
||||||
|
<Form.Item required label="选择Commit ID">
|
||||||
|
<Select value={extra2} placeholder="请选择" onChange={v => setExtra2(v)}>
|
||||||
|
{extra1 && branches ? branches[extra1].map(item => (
|
||||||
|
<Select.Option key={item.id}>
|
||||||
|
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
||||||
|
<span style={{
|
||||||
|
width: 400,
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis'
|
||||||
|
}}>{item.id.substr(0, 6)} {item['author']} {item['message']}</span>
|
||||||
|
<span style={{color: '#999', fontSize: 12}}>{item['date']} </span>
|
||||||
|
</div>
|
||||||
|
</Select.Option>
|
||||||
|
)) : null}
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
)}
|
||||||
<Form.Item required label="目标主机" tooltip="可以通过创建多个发布申请单,选择主机分批发布。">
|
<Form.Item required label="目标主机" tooltip="可以通过创建多个发布申请单,选择主机分批发布。">
|
||||||
{host_ids.length > 0 && `已选择 ${host_ids.length} 台(可选${app_host_ids.length})`}
|
{host_ids.length > 0 && `已选择 ${host_ids.length} 台(可选${app_host_ids.length})`}
|
||||||
<Button type="link" onClick={() => setVisible(true)}>选择主机</Button>
|
<Button type="link" onClick={() => setVisible(true)}>选择主机</Button>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { BranchesOutlined, BuildOutlined, TagOutlined, PlusOutlined } from '@ant-design/icons';
|
import { BranchesOutlined, BuildOutlined, TagOutlined, PlusOutlined, TagsOutlined } from '@ant-design/icons';
|
||||||
import { Radio, Modal, Popover, Tag, Popconfirm, Tooltip, message } from 'antd';
|
import { Radio, Modal, Popover, Tag, Popconfirm, Tooltip, message } from 'antd';
|
||||||
import { http, hasPermission } from 'libs';
|
import { http, hasPermission } from 'libs';
|
||||||
import { Action, AuthButton, TableCard } from 'components';
|
import { Action, AuthButton, TableCard } from 'components';
|
||||||
|
@ -33,17 +33,18 @@ function ComTable() {
|
||||||
title: '版本',
|
title: '版本',
|
||||||
render: info => {
|
render: info => {
|
||||||
if (info['app_extend'] === '1') {
|
if (info['app_extend'] === '1') {
|
||||||
const [ext1] = info.rep_extra;
|
const [ext1] = info.extra || info.rep_extra;
|
||||||
return (
|
switch (ext1) {
|
||||||
<React.Fragment>
|
case 'branch':
|
||||||
{ext1 === 'branch' ? <BranchesOutlined/> : <TagOutlined/>} {info.version}
|
return <div><BranchesOutlined/> {info.version}</div>
|
||||||
</React.Fragment>
|
case 'tag':
|
||||||
)
|
return <div><TagOutlined/> {info.version}</div>
|
||||||
|
default:
|
||||||
|
return <div><TagsOutlined/> {info.version}</div>
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<div><BuildOutlined/> {info.version}</div>
|
||||||
<BuildOutlined/> {info.version}
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue