mirror of https://github.com/openspug/spug
upgrade deploy
parent
d076c51c3d
commit
4d86288ab5
|
@ -74,10 +74,7 @@ class DeployExtend1(models.Model, ModelMixin):
|
|||
deploy = models.OneToOneField(Deploy, primary_key=True, on_delete=models.CASCADE)
|
||||
git_repo = models.CharField(max_length=255)
|
||||
dst_dir = models.CharField(max_length=255)
|
||||
dst_repo = models.CharField(max_length=255)
|
||||
versions = models.IntegerField()
|
||||
filter_rule = models.TextField()
|
||||
custom_envs = models.TextField()
|
||||
hook_pre_server = models.TextField(null=True)
|
||||
hook_post_server = models.TextField(null=True)
|
||||
hook_pre_host = models.TextField(null=True)
|
||||
|
@ -86,7 +83,6 @@ class DeployExtend1(models.Model, ModelMixin):
|
|||
def to_dict(self, *args, **kwargs):
|
||||
tmp = super().to_dict(*args, **kwargs)
|
||||
tmp['filter_rule'] = json.loads(self.filter_rule)
|
||||
tmp['custom_envs'] = '\n'.join(f'{k}={v}' for k, v in json.loads(self.custom_envs).items())
|
||||
return tmp
|
||||
|
||||
def __repr__(self):
|
||||
|
|
|
@ -120,10 +120,7 @@ class DeployView(View):
|
|||
extend_form, error = JsonParser(
|
||||
Argument('git_repo', handler=str.strip, help='请输入git仓库地址'),
|
||||
Argument('dst_dir', handler=str.strip, help='请输入发布目标路径'),
|
||||
Argument('dst_repo', handler=str.strip, help='请输入目标仓库路径'),
|
||||
Argument('versions', type=int, help='请输入保留历史版本数量'),
|
||||
Argument('filter_rule', type=dict, help='参数错误'),
|
||||
Argument('custom_envs', handler=str.strip, required=False),
|
||||
Argument('hook_pre_server', handler=str.strip, default=''),
|
||||
Argument('hook_post_server', handler=str.strip, default=''),
|
||||
Argument('hook_pre_host', handler=str.strip, default=''),
|
||||
|
@ -133,7 +130,6 @@ class DeployView(View):
|
|||
return json_response(error=error)
|
||||
extend_form.dst_dir = extend_form.dst_dir.rstrip('/')
|
||||
extend_form.filter_rule = json.dumps(extend_form.filter_rule)
|
||||
extend_form.custom_envs = json.dumps(parse_envs(extend_form.custom_envs))
|
||||
if form.id:
|
||||
extend = DeployExtend1.objects.filter(deploy_id=form.id).first()
|
||||
if extend.git_repo != extend_form.git_repo:
|
||||
|
|
|
@ -19,8 +19,7 @@ class AddSelect extends React.Component {
|
|||
git_type: 'branch',
|
||||
is_audit: false,
|
||||
rst_notify: {mode: '0'},
|
||||
versions: 10,
|
||||
host_ids: [undefined],
|
||||
host_ids: [],
|
||||
filter_rule: {type: 'contain', data: ''}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -23,15 +23,15 @@ export default observer(function Ext1From() {
|
|||
return (
|
||||
<Modal
|
||||
visible
|
||||
width={900}
|
||||
width={800}
|
||||
maskClosable={false}
|
||||
title={title}
|
||||
onCancel={() => store.ext1Visible = false}
|
||||
footer={null}>
|
||||
<Steps current={store.page} className={styles.steps}>
|
||||
<Steps.Step key={0} title="基本配置"/>
|
||||
<Steps.Step key={1} title="发布主机"/>
|
||||
<Steps.Step key={2} title="任务配置"/>
|
||||
<Steps.Step key={1} title="构建配置"/>
|
||||
<Steps.Step key={2} title="发布配置"/>
|
||||
</Steps>
|
||||
{store.page === 0 && <Setup1/>}
|
||||
{store.page === 1 && <Setup2/>}
|
||||
|
|
|
@ -8,6 +8,7 @@ import { observer } from 'mobx-react';
|
|||
import { Link } from 'react-router-dom';
|
||||
import { Switch, Form, Input, Select, Button } from 'antd';
|
||||
import envStore from 'pages/config/environment/store';
|
||||
import Selector from 'pages/host/Selector';
|
||||
import store from './store';
|
||||
|
||||
export default observer(function Ext1Setup1() {
|
||||
|
@ -41,6 +42,10 @@ export default observer(function Ext1Setup1() {
|
|||
<Link disabled={store.isReadOnly} to="/config/environment">新建环境</Link>
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
<Form.Item required label="目标主机">
|
||||
{info.host_ids.length > 0 && `已选择 ${info.host_ids.length} 台`}
|
||||
<Button type="link" onClick={() => store.selectorVisible = true}>选择主机</Button>
|
||||
</Form.Item>
|
||||
<Form.Item required label="Git仓库地址">
|
||||
<Input disabled={store.isReadOnly} value={info['git_repo']} onChange={e => info['git_repo'] = e.target.value}
|
||||
placeholder="请输入Git仓库地址"/>
|
||||
|
@ -60,9 +65,10 @@ export default observer(function Ext1Setup1() {
|
|||
</span>}>
|
||||
<Input
|
||||
addonBefore={(
|
||||
<Select disabled={store.isReadOnly}
|
||||
value={info['rst_notify']['mode']} style={{width: 100}}
|
||||
onChange={v => info['rst_notify']['mode'] = v}>
|
||||
<Select
|
||||
disabled={store.isReadOnly}
|
||||
value={info['rst_notify']['mode']} style={{width: 100}}
|
||||
onChange={v => info['rst_notify']['mode'] = v}>
|
||||
<Select.Option value="0">关闭</Select.Option>
|
||||
<Select.Option value="1">钉钉</Select.Option>
|
||||
<Select.Option value="3">企业微信</Select.Option>
|
||||
|
@ -77,9 +83,14 @@ export default observer(function Ext1Setup1() {
|
|||
<Form.Item wrapperCol={{span: 14, offset: 6}}>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={!(info.env_id && info['git_repo'])}
|
||||
disabled={!(info.env_id && info.git_repo && info.host_ids.length)}
|
||||
onClick={() => store.page += 1}>下一步</Button>
|
||||
</Form.Item>
|
||||
<Selector
|
||||
visible={store.selectorVisible}
|
||||
selectedRowKeys={[...info.host_ids]}
|
||||
onCancel={() => store.selectorVisible = false}
|
||||
onOk={(_, ids) => info.host_ids = ids}/>
|
||||
</Form>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -5,85 +5,99 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Form, Input, Select, Button, message } from "antd";
|
||||
import { hasHostPermission } from 'libs';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { Form, Radio, Button, Tooltip } from "antd";
|
||||
import { cleanCommand } from 'libs';
|
||||
import Editor from 'react-ace';
|
||||
import 'ace-builds/src-noconflict/mode-text';
|
||||
import 'ace-builds/src-noconflict/mode-sh';
|
||||
import 'ace-builds/src-noconflict/theme-tomorrow';
|
||||
import store from './store';
|
||||
import hostStore from 'pages/host/store';
|
||||
import styles from './index.module.css';
|
||||
|
||||
@observer
|
||||
class Ext1Setup2 extends React.Component {
|
||||
componentDidMount() {
|
||||
if (hostStore.records.length === 0) {
|
||||
hostStore.fetchRecords()
|
||||
}
|
||||
export default observer(function () {
|
||||
const Tips = (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://spug.dev/docs/deploy-config/#%E5%85%A8%E5%B1%80%E5%8F%98%E9%87%8F">内置全局变量</a>
|
||||
)
|
||||
|
||||
function handleNext() {
|
||||
store.page += 1
|
||||
}
|
||||
|
||||
checkStatus = () => {
|
||||
const info = store.deploy;
|
||||
return info['dst_dir'] && info['dst_repo'] && info['versions'] && info['host_ids'].filter(x => x).length > 0
|
||||
};
|
||||
const FilterHead = (
|
||||
<div style={{width: 512, display: 'flex', justifyContent: 'space-between'}}>
|
||||
<span>
|
||||
文件过滤规则
|
||||
<Tooltip title="请输入相对于项目根目录的文件路径,根据包含或排除规则进行打包。">
|
||||
<QuestionCircleOutlined style={{color: 'rgba(0, 0, 0, 0.45)'}}/>
|
||||
</Tooltip>
|
||||
</span>
|
||||
<Radio.Group
|
||||
size="small"
|
||||
value={store.deploy.filter_rule.type}
|
||||
onChange={e => store.deploy.filter_rule.type = e.target.value}>
|
||||
<Radio.Button value="contain">
|
||||
<Tooltip title="仅打包匹配到的文件或目录,如果内容为空则打包所有。">包含</Tooltip>
|
||||
</Radio.Button>
|
||||
<Radio.Button value="exclude">
|
||||
<Tooltip title="打包时排除匹配到的文件或目录,如果内容为空则不排除任何文件。">排除</Tooltip>
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
)
|
||||
|
||||
handleNext = () => {
|
||||
const {dst_dir, dst_repo} = store.deploy;
|
||||
if (dst_repo.includes(dst_dir.replace(/\/*$/, '/'))) {
|
||||
message.error('仓库目录不能位于发布部署目录内')
|
||||
} else {
|
||||
store.page += 1
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const info = store.deploy;
|
||||
return (
|
||||
<Form labelCol={{span: 6}} wrapperCol={{span: 14}}>
|
||||
<Form.Item required label="目标主机部署路径" help="目标主机的应用根目录,例如:/var/www/html">
|
||||
<Input disabled={store.isReadOnly} value={info['dst_dir']} onChange={e => info['dst_dir'] = e.target.value}
|
||||
placeholder="请输入目标主机部署路径"/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="目标主机仓库路径" help="此目录用于存储应用的历史版本,例如:/data/spug/repos">
|
||||
<Input disabled={store.isReadOnly} value={info['dst_repo']} onChange={e => info['dst_repo'] = e.target.value} placeholder="请输入目标主机仓库路径"/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="保留历史版本数量" help="早于指定数量的历史版本会被删除,以释放空间">
|
||||
<Input disabled={store.isReadOnly} value={info['versions']} onChange={e => info['versions'] = e.target.value} placeholder="请输入保留历史版本数量"/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="发布目标主机">
|
||||
{info['host_ids'].map((id, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<Select
|
||||
value={id}
|
||||
showSearch
|
||||
placeholder="请选择"
|
||||
disabled={store.isReadOnly}
|
||||
style={{width: '80%', marginRight: 10, marginBottom: 12}}
|
||||
optionFilterProp="children"
|
||||
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
|
||||
onChange={v => store.editHost(index, v)}>
|
||||
{hostStore.records.filter(x => hasHostPermission(x.id)).map(item => (
|
||||
<Select.Option key={item.id} value={item.id} disabled={info['host_ids'].includes(item.id)}>
|
||||
{`${item.name}(${item['hostname']}:${item['port']})`}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
{!store.isReadOnly && info['host_ids'].length > 1 && (
|
||||
<MinusCircleOutlined className={styles.delIcon} onClick={() => store.delHost(index)} />
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Form.Item>
|
||||
<Form.Item wrapperCol={{span: 14, offset: 6}}>
|
||||
<Button disabled={store.isReadOnly} type="dashed" style={{width: '80%'}} onClick={store.addHost}>
|
||||
<PlusOutlined />添加目标主机
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item wrapperCol={{span: 14, offset: 6}}>
|
||||
<Button disabled={!this.checkStatus()} type="primary" onClick={this.handleNext}>下一步</Button>
|
||||
<Button style={{marginLeft: 20}} onClick={() => store.page -= 1}>上一步</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Ext1Setup2
|
||||
const info = store.deploy;
|
||||
return (
|
||||
<Form layout="vertical" style={{padding: '0 120px'}}>
|
||||
<Form.Item label={FilterHead} tooltip="xxx">
|
||||
<Editor
|
||||
readOnly={store.isReadOnly}
|
||||
mode="text"
|
||||
theme="tomorrow"
|
||||
width="100%"
|
||||
height="80px"
|
||||
placeholder="每行一条规则"
|
||||
value={info['filter_rule']['data']}
|
||||
onChange={v => info['filter_rule']['data'] = cleanCommand(v)}
|
||||
style={{border: '1px solid #e8e8e8'}}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="代码检出前执行"
|
||||
tooltip="在运行 Spug 的服务器(或容器)上执行,当前目录为仓库源代码目录,可以执行任意自定义命令。"
|
||||
help={<span>可使用 {Tips},请避免在此修改已跟踪的文件,防止在检出代码时失败。</span>}>
|
||||
<Editor
|
||||
readOnly={store.isReadOnly}
|
||||
mode="sh"
|
||||
theme="tomorrow"
|
||||
width="100%"
|
||||
height="120px"
|
||||
placeholder="输入要执行的命令"
|
||||
value={info['hook_pre_server']}
|
||||
onChange={v => info['hook_pre_server'] = cleanCommand(v)}
|
||||
style={{border: '1px solid #e8e8e8'}}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="代码检出后执行"
|
||||
style={{marginTop: 12, marginBottom: 24}}
|
||||
tooltip="在运行 Spug 的服务器(或容器)上执行,当前目录为检出后的源代码目录,可执行任意自定义命令。"
|
||||
help={<span>可使用 {Tips},大多数情况下在此进行构建操作。</span>}>
|
||||
<Editor
|
||||
readOnly={store.isReadOnly}
|
||||
mode="sh"
|
||||
theme="tomorrow"
|
||||
width="100%"
|
||||
height="120px"
|
||||
placeholder="输入要执行的命令"
|
||||
value={info['hook_post_server']}
|
||||
onChange={v => info['hook_post_server'] = cleanCommand(v)}
|
||||
style={{border: '1px solid #e8e8e8'}}/>
|
||||
</Form.Item>
|
||||
<Form.Item wrapperCol={{span: 14, offset: 6}}>
|
||||
<Button type="primary" onClick={handleNext}>下一步</Button>
|
||||
<Button style={{marginLeft: 20}} onClick={() => store.page -= 1}>上一步</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -3,210 +3,79 @@
|
|||
* Copyright (c) <spug.dev@gmail.com>
|
||||
* Released under the AGPL-3.0 License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import React, {useState} from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { GitlabOutlined, InfoCircleOutlined, SettingOutlined, SwapOutlined } from '@ant-design/icons';
|
||||
import { Form, Row, Col, Button, Radio, Tooltip, message } from 'antd';
|
||||
import { LinkButton } from 'components';
|
||||
import { Form, Button, Input, message } from 'antd';
|
||||
import Editor from 'react-ace';
|
||||
import 'ace-builds/src-noconflict/mode-text';
|
||||
import 'ace-builds/src-noconflict/mode-sh';
|
||||
import 'ace-builds/src-noconflict/theme-tomorrow';
|
||||
import { http, cleanCommand } from 'libs';
|
||||
import store from './store';
|
||||
import http from 'libs/http';
|
||||
import styles from './index.module.css';
|
||||
import { cleanCommand } from "../../../libs";
|
||||
|
||||
@observer
|
||||
class Ext1Setup3 extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.helpMap = {
|
||||
'2': <span>
|
||||
Spug 内置了一些全局变量,这些变量可以直接使用,请参考官方文档:
|
||||
<a target="_blank" rel="noopener noreferrer"
|
||||
href="https://spug.dev/docs/deploy-config/#%E5%85%A8%E5%B1%80%E5%8F%98%E9%87%8F">全局变量</a>
|
||||
</span>,
|
||||
'3': '在部署 Spug 的服务器上运行,可以执行任意自定义命令。',
|
||||
'4': '在部署 Spug 的服务器上运行,当前目录为检出后待发布的源代码目录,可执行任意自定义命令。',
|
||||
'5': '在发布的目标主机上运行,当前目录为目标主机上待发布的源代码目录,可执行任意自定义命令。',
|
||||
'6': '在发布的目标主机上运行,当前目录为已发布的应用目录,可执行任意自定义命令。'
|
||||
};
|
||||
this.state = {
|
||||
loading: false,
|
||||
full: ''
|
||||
}
|
||||
}
|
||||
export default observer(function () {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const Tips = (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://spug.dev/docs/deploy-config/#%E5%85%A8%E5%B1%80%E5%8F%98%E9%87%8F">内置全局变量</a>
|
||||
)
|
||||
|
||||
handleSubmit = () => {
|
||||
this.setState({loading: true});
|
||||
function handleSubmit() {
|
||||
setLoading(true);
|
||||
const info = store.deploy;
|
||||
info['app_id'] = store.app_id;
|
||||
info['extend'] = '1';
|
||||
info['host_ids'] = info['host_ids'].filter(x => x);
|
||||
http.post('/api/app/deploy/', info)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
store.loadDeploys(store.app_id);
|
||||
store.ext1Visible = false
|
||||
}, () => this.setState({loading: false}))
|
||||
};
|
||||
|
||||
handleFullscreen = (id) => {
|
||||
if (this.state.full) {
|
||||
this.setState({full: ''})
|
||||
} else {
|
||||
this.setState({full: id})
|
||||
}
|
||||
}, () => setLoading(false))
|
||||
}
|
||||
|
||||
FilterLabel = (props) => (
|
||||
<div style={{display: 'flex', alignItems: 'center', height: 40}}>
|
||||
<div>文件过滤 :</div>
|
||||
<Radio.Group
|
||||
disabled={store.isReadOnly}
|
||||
style={{marginLeft: 20, float: 'left'}}
|
||||
value={props.type}
|
||||
onChange={e => store.deploy['filter_rule']['type'] = e.target.value}>
|
||||
<Radio value="contain">包含
|
||||
<Tooltip title="请输入相对于项目根目录的文件路径,仅将匹配到文件传输至要发布的目标主机。">
|
||||
<InfoCircleOutlined style={{color: '#515151', marginLeft: 8}}/>
|
||||
</Tooltip>
|
||||
</Radio>
|
||||
<Radio value="exclude">排除
|
||||
<Tooltip title="支持模糊匹配,如果路径以 / 开头则基于项目根目录匹配,匹配到文件将不会被传输。">
|
||||
<InfoCircleOutlined style={{color: '#515151', marginLeft: 8}}/>
|
||||
</Tooltip>
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
<div style={{flex: 1, textAlign: 'right'}}>
|
||||
<LinkButton onClick={() => this.handleFullscreen('1')}>{this.state.full ? '退出全屏' : '全屏'}</LinkButton>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
NormalLabel = (props) => (
|
||||
<div style={{display: 'flex', alignItems: 'center', height: 40}}>
|
||||
<div style={{marginRight: 8}}>{props.title} :</div>
|
||||
<Tooltip title={this.helpMap[props.id]}>
|
||||
<InfoCircleOutlined style={{color: '#515151'}}/>
|
||||
</Tooltip>
|
||||
<div style={{flex: 1, textAlign: 'right'}}>
|
||||
<LinkButton onClick={() => this.handleFullscreen(props.id)}>{this.state.full ? '退出全屏' : '全屏'}</LinkButton>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
render() {
|
||||
const info = store.deploy;
|
||||
const {full} = this.state;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Row>
|
||||
<Col span={11}>
|
||||
<div className={full === '1' ? styles.fullScreen : null} style={{marginBottom: 24}}>
|
||||
<this.FilterLabel type={info['filter_rule']['type']}/>
|
||||
<Editor
|
||||
readOnly={store.isReadOnly}
|
||||
mode="text"
|
||||
theme="tomorrow"
|
||||
width="100%"
|
||||
height={full === '1' ? '100vh' : '100px'}
|
||||
placeholder="每行一条规则"
|
||||
value={info['filter_rule']['data']}
|
||||
onChange={v => info['filter_rule']['data'] = cleanCommand(v)}
|
||||
style={{border: '1px solid #e8e8e8'}}/>
|
||||
</div>
|
||||
<div className={full === '3' ? styles.fullScreen : null} style={{marginBottom: 24}}>
|
||||
<this.NormalLabel title="代码检出前执行" id="3"/>
|
||||
<Editor
|
||||
readOnly={store.isReadOnly}
|
||||
mode="sh"
|
||||
theme="tomorrow"
|
||||
width="100%"
|
||||
height={full === '3' ? '100vh' : '100px'}
|
||||
placeholder="输入要执行的命令"
|
||||
value={info['hook_pre_server']}
|
||||
onChange={v => info['hook_pre_server'] = cleanCommand(v)}
|
||||
style={{border: '1px solid #e8e8e8'}}/>
|
||||
</div>
|
||||
<div className={full === '5' ? styles.fullScreen : null} style={{marginBottom: 24}}>
|
||||
<this.NormalLabel title="应用发布前执行" id="5"/>
|
||||
<Editor
|
||||
readOnly={store.isReadOnly}
|
||||
mode="sh"
|
||||
theme="tomorrow"
|
||||
width="100%"
|
||||
height={full === '5' ? '100vh' : '100px'}
|
||||
placeholder="输入要执行的命令"
|
||||
value={info['hook_pre_host']}
|
||||
onChange={v => info['hook_pre_host'] = cleanCommand(v)}
|
||||
style={{border: '1px solid #e8e8e8'}}/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<div className={styles.deployBlock} style={{marginTop: 39}}>
|
||||
<SettingOutlined style={{fontSize: 32}}/>
|
||||
<span style={{fontSize: 12, marginTop: 5}}>基础设置</span>
|
||||
</div>
|
||||
<div className={styles.deployBlock}>
|
||||
<GitlabOutlined style={{fontSize: 32}}/>
|
||||
<span style={{fontSize: 12, marginTop: 5}}>检出代码</span>
|
||||
</div>
|
||||
<div className={styles.deployBlock}>
|
||||
<SwapOutlined style={{fontSize: 32}}/>
|
||||
<span style={{fontSize: 12, marginTop: 5}}>版本切换</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={11}>
|
||||
<div className={full === '2' ? styles.fullScreen : null} style={{marginBottom: 24}}>
|
||||
<this.NormalLabel title="自定义全局变量" id="2"/>
|
||||
<Editor
|
||||
readOnly={store.isReadOnly}
|
||||
mode="text"
|
||||
theme="tomorrow"
|
||||
width="100%"
|
||||
height={full === '2' ? '100vh' : '100px'}
|
||||
placeholder="每行一个,例如:HOME=/data/spug"
|
||||
value={info['custom_envs']}
|
||||
onChange={v => info['custom_envs'] = cleanCommand(v)}
|
||||
style={{border: '1px solid #e8e8e8'}}/>
|
||||
</div>
|
||||
<div className={full === '4' ? styles.fullScreen : null} style={{marginBottom: 24}}>
|
||||
<this.NormalLabel title="代码检出后执行" id="4"/>
|
||||
<Editor
|
||||
readOnly={store.isReadOnly}
|
||||
mode="sh"
|
||||
theme="tomorrow"
|
||||
width="100%"
|
||||
height={full === '4' ? '100vh' : '100px'}
|
||||
placeholder="输入要执行的命令"
|
||||
value={info['hook_post_server']}
|
||||
onChange={v => info['hook_post_server'] = cleanCommand(v)}
|
||||
style={{border: '1px solid #e8e8e8'}}/>
|
||||
</div>
|
||||
<div className={full === '6' ? styles.fullScreen : null} style={{marginBottom: 24}}>
|
||||
<this.NormalLabel title="应用发布后执行" id="6"/>
|
||||
<Editor
|
||||
readOnly={store.isReadOnly}
|
||||
mode="sh"
|
||||
theme="tomorrow"
|
||||
width="100%"
|
||||
height={full === '6' ? '100vh' : '100px'}
|
||||
placeholder="输入要执行的命令"
|
||||
value={info['hook_post_host']}
|
||||
onChange={v => info['hook_post_host'] = cleanCommand(v)}
|
||||
style={{border: '1px solid #e8e8e8'}}/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item wrapperCol={{span: 14, offset: 6}}>
|
||||
<Button disabled={store.isReadOnly} type="primary" onClick={this.handleSubmit}>提交</Button>
|
||||
<Button style={{marginLeft: 20}} onClick={() => store.page -= 1}>上一步</Button>
|
||||
</Form.Item>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Ext1Setup3
|
||||
const info = store.deploy;
|
||||
return (
|
||||
<Form layout="vertical" style={{padding: '0 120px'}}>
|
||||
<Form.Item required label="部署目标路径" tooltip="应用最终在主机上部署路径,构建的结果将会放置于该路径下。">
|
||||
<Input value={info['dst_dir']} onChange={e => info['dst_dir'] = e.target.value} placeholder="请输入部署目标路径" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="应用发布前执行"
|
||||
tooltip="在发布的目标主机上运行,当前目录为目标主机上待发布的源代码目录,可执行任意自定义命令。"
|
||||
help={<span>可使用 {Tips},此时还未进行文件变更,可进行一些发布前置操作。</span>}>
|
||||
<Editor
|
||||
readOnly={store.isReadOnly}
|
||||
mode="sh"
|
||||
theme="tomorrow"
|
||||
width="100%"
|
||||
height="150px"
|
||||
placeholder="输入要执行的命令"
|
||||
value={info['hook_pre_host']}
|
||||
onChange={v => info['hook_pre_host'] = cleanCommand(v)}
|
||||
style={{border: '1px solid #e8e8e8'}}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="应用发布后执行"
|
||||
style={{marginTop: 12, marginBottom: 24}}
|
||||
tooltip="在发布的目标主机上运行,当前目录为已发布的应用目录,可执行任意自定义命令。"
|
||||
help={<span>可使用 {Tips},可以在发布后进行重启服务等操作。</span>}>
|
||||
<Editor
|
||||
readOnly={store.isReadOnly}
|
||||
mode="sh"
|
||||
theme="tomorrow"
|
||||
width="100%"
|
||||
height="150px"
|
||||
placeholder="输入要执行的命令"
|
||||
value={info['hook_post_host']}
|
||||
onChange={v => info['hook_post_host'] = cleanCommand(v)}
|
||||
style={{border: '1px solid #e8e8e8'}}/>
|
||||
</Form.Item>
|
||||
<Form.Item wrapperCol={{span: 14, offset: 6}}>
|
||||
<Button disabled={store.isReadOnly} loading={loading} type="primary" onClick={handleSubmit}>提交</Button>
|
||||
<Button style={{marginLeft: 20}} onClick={() => store.page -= 1}>上一步</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
)
|
||||
})
|
|
@ -91,7 +91,7 @@ class ComTable extends React.Component {
|
|||
};
|
||||
|
||||
expandedRowRender = (record) => {
|
||||
if (record['deploys'] === undefined) {
|
||||
if (!record.isLoaded) {
|
||||
store.loadDeploys(record.id)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ class Store {
|
|||
@observable addVisible = false;
|
||||
@observable ext1Visible = false;
|
||||
@observable ext2Visible = false;
|
||||
@observable selectorVisible = false;
|
||||
|
||||
@observable f_name;
|
||||
@observable f_desc;
|
||||
|
@ -47,6 +48,7 @@ class Store {
|
|||
};
|
||||
|
||||
loadDeploys = (app_id) => {
|
||||
this.records[`a${app_id}`].isLoaded = true;
|
||||
return http.get('/api/app/deploy/', {params: {app_id}})
|
||||
.then(res => this.records[`a${app_id}`]['deploys'] = res)
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue