A show publish info

This commit is contained in:
zhenorzz 2022-05-13 19:29:06 +08:00
parent 2ecf1a7516
commit 79cba51e21
12 changed files with 139 additions and 63 deletions

View File

@ -34,6 +34,8 @@ type PublishTraces []PublishTrace
// publish trace state
const (
QUEUE = 0
BeforePull = 1
Pull = 2
@ -182,10 +184,14 @@ func (pt PublishTrace) GetPreview(
pagination Pagination,
) (PublishTraces, Pagination, error) {
builder := sq.
Select("id, token, project_id, project_name, state, publisher_id, publisher_name, type, ext, insert_time, update_time").
Column("!EXISTS (SELECT id FROM " + publishTraceTable + " AS pt where pt.state = 0 AND pt.token = publish_trace.token) as publish_state").
From(publishTraceTable).
Where(sq.Eq{"type": Pull})
Select(
"token",
"MIN(publisher_name) publisher_name",
"MIN(state) publish_state",
"GROUP_CONCAT(IF(type = 2 and ext != '', JSON_EXTRACT(ext, '$.commit') , '') SEPARATOR '') as ext",
"MIN(update_time) update_time",
).
From(publishTraceTable)
if pt.ProjectID != 0 {
builder = builder.Where(sq.Eq{"project_id": pt.ProjectID})
}
@ -211,6 +217,7 @@ func (pt PublishTrace) GetPreview(
builder = builder.Having(sq.Eq{"publish_state": pt.PublishState})
}
rows, err := builder.RunWith(DB).
GroupBy("token").
OrderBy("update_time DESC").
Limit(pagination.Rows).
Offset((pagination.Page - 1) * pagination.Rows).
@ -223,18 +230,11 @@ func (pt PublishTrace) GetPreview(
var publishTrace PublishTrace
if err := rows.Scan(
&publishTrace.ID,
&publishTrace.Token,
&publishTrace.ProjectID,
&publishTrace.ProjectName,
&publishTrace.State,
&publishTrace.PublisherID,
&publishTrace.PublisherName,
&publishTrace.Type,
&publishTrace.PublishState,
&publishTrace.Ext,
&publishTrace.InsertTime,
&publishTrace.UpdateTime,
&publishTrace.PublishState); err != nil {
&publishTrace.UpdateTime); err != nil {
return nil, pagination, err
}
publishTraces = append(publishTraces, publishTrace)

View File

@ -5,7 +5,6 @@
package service
import (
"bytes"
"errors"
"github.com/zhenorzz/goploy/core"
"github.com/zhenorzz/goploy/model"
@ -52,7 +51,7 @@ func (rt rsyncTransmitter) Args() []string {
func (rt rsyncTransmitter) String() string {
logRsyncCmd := regexp.MustCompile(`sshpass -p .*\s`).
ReplaceAllString("rsync "+strings.Join(rt.Args(), " "), "sshpass -p ***** ")
ReplaceAllString(exec.Command("rsync", rt.Args()...).String(), "sshpass -p ***** ")
return logRsyncCmd
}
@ -60,11 +59,9 @@ func (rt rsyncTransmitter) Exec() (string, error) {
// example
// rsync -rtv -e "ssh -o StrictHostKeyChecking=no -p 22 -i C:\Users\Administrator\.ssh\id_rsa" --rsync-path="mkdir -p /data/www/test && rsync" ./main.go root@127.0.0.1:/tmp/test/
cmd := exec.Command("rsync", rt.Args()...)
var outbuf, errbuf bytes.Buffer
cmd.Stdout = &outbuf
cmd.Stderr = &errbuf
if err := cmd.Run(); err != nil {
return outbuf.String(), errors.New("err: " + err.Error() + ", detail: " + errbuf.String())
if output, err := cmd.CombinedOutput(); err != nil {
return "", errors.New("err: " + err.Error() + "\noutput: " + string(output))
} else {
return string(output), nil
}
return outbuf.String(), nil
}

View File

@ -7,6 +7,7 @@ package task
import (
"container/list"
"github.com/zhenorzz/goploy/config"
"github.com/zhenorzz/goploy/model"
"github.com/zhenorzz/goploy/service"
"github.com/zhenorzz/goploy/ws"
"sync"
@ -49,8 +50,25 @@ func startDeployTask() {
func AddDeployTask(gsync service.Gsync) {
ws.GetHub().Data <- &ws.Data{
Type: ws.TypeProject,
Message: ws.ProjectMessage{ProjectID: gsync.Project.ID, ProjectName: gsync.Project.Name, State: ws.TaskWaiting, Message: "Task waiting"},
Type: ws.TypeProject,
Message: ws.ProjectMessage{
ProjectID: gsync.Project.ID,
ProjectName: gsync.Project.Name,
State: ws.TaskWaiting,
Message: "Task waiting",
Ext: struct {
LastPublishToken string `json:"lastPublishToken"`
}{gsync.Project.LastPublishToken},
},
}
model.PublishTrace{
Token: gsync.Project.LastPublishToken,
ProjectID: gsync.Project.ID,
ProjectName: gsync.Project.Name,
PublisherID: gsync.UserInfo.ID,
PublisherName: gsync.UserInfo.Name,
Type: model.QUEUE,
State: model.Success,
}.AddRow()
deployList.PushBack(gsync)
}

View File

@ -11,7 +11,7 @@ import { logout } from '@/utils/auth'
const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API, // url = base url + request url
withCredentials: true, // send cookies when cross-domain requests
timeout: 5000, // request timeout
timeout: 60000, // request timeout
})
// request interceptor

View File

@ -18,7 +18,8 @@ export interface ProjectData {
afterPullScript: string
afterDeployScriptMode: string
afterDeployScript: string
rsyncOption: string
transferType: string
transferOption: string
autoDeploy: number
publisherId: number
publisherName: string
@ -158,7 +159,8 @@ export class ProjectAdd extends Request {
afterPullScript: string
afterDeployScriptMode: string
afterDeployScript: string
rsyncOption: string
transferType: string
transferOption: string
serverIds: number[]
userIds: number[]
notifyType: number
@ -188,7 +190,8 @@ export class ProjectEdit extends Request {
afterPullScript: string
afterDeployScriptMode: string
afterDeployScript: string
rsyncOption: string
transferType: string
transferOption: string
notifyType: number
notifyTarget: string
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1652436746546" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8557" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
</style></defs><path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474c-6.1-7.7-15.3-12.2-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1 0.4-12.8-6.3-12.8z" p-id="8558"></path></svg>

After

Width:  |  Height:  |  Size: 913 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1652436937346" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8696" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
</style></defs><path d="M563.8 512l262.5-312.9c4.4-5.2 0.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9c-4.4 5.2-0.7 13.1 6.1 13.1h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z" p-id="8697"></path></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -299,8 +299,11 @@
"projectPage": {
"testConnection": "Test",
"lishBranch": "List branch",
"transferType": "Transfer type",
"transferOption": "Transfer option",
"scriptMode": "Script mode",
"rsyncDoc": "https://linux.die.net/man/1/rsync",
"sftpDoc": "https://docs.goploy.icu/#/en/dependency/sftp",
"deployNotice": "Deploy notice",
"publishReview": "Review",
"reviewFooterTips": "Only members deploy projects will trigger review\n1. Go to the Deploy page for review\n2. Push to URL:http(s)://domain?custom-param=1&callback=***\n3. Access the callback value\nRepeated access callback will only be published once",

View File

@ -295,8 +295,11 @@
"projectPage": {
"testConnection": "测试连接",
"lishBranch": "列出分支",
"transferType": "传输方式",
"transferOption": "传输选项",
"scriptMode": "脚本类型",
"rsyncDoc": "https://docs.goploy.icu/#/dependency/rsync",
"sftpDoc": "https://docs.goploy.icu/#/dependency/sftp",
"deployNotice": "构建通知",
"publishReview": "发布审核",
"reviewFooterTips": "只有成员构建项目才会触发审核\n审核方式\n1. 前往构建发布页面进行审核\n2. 推送到URL:http(s)://domain?custom-param=1&callback=***\n3. http get callback的值即可完成审核\n重复访问callback只会发布一次并且发布过不会再次发布",

View File

@ -3,7 +3,8 @@
v-model="dialogVisible"
:title="$t('detail')"
:fullscreen="$store.state.app.device === 'mobile'"
@close="filterInpurtVisible = false"
width="75%"
@close="onClose"
>
<el-row type="flex">
<div v-loading="filterloading" class="publish-preview">
@ -152,14 +153,27 @@
<el-radio class="publish-commit" :label="item.token" border>
<span class="publish-name">{{ item.publisherName }}</span>
<span class="publish-commitID">
commitID: {{ item.commit }}
<span v-if="projectRow.repoType === 'svn'">
revision: {{ item['commit'] }}
</span>
<span v-else-if="projectRow.repoType === 'sftp'">
uuid: {{ item['token'].substring(0, 6) }}
</span>
<span v-else-if="projectRow.repoType === 'ftp'">
uuid: {{ item['token'].substring(0, 6) }}
</span>
<span v-else>commitID: {{ item['commit'] }}</span>
</span>
<span
<SvgIcon
v-if="item.publishState === 1"
class="icon-success"
style="float: right"
/>
<span v-else class="icon-fail" style="float: right" />
style="color: #67c23a; font-size: 15px; float: right"
icon-class="check"
></SvgIcon>
<SvgIcon
v-else
style="color: #f56c6c; font-size: 15px; float: right"
icon-class="close"
></SvgIcon>
</el-radio>
<el-button type="danger" plain @click="rebuild(item)">
rollback
@ -240,11 +254,8 @@
<div>Script:</div>
<pre style="white-space: pre-line">{{ item.script }}</pre>
</el-row>
<el-row
v-loading="traceDetail[item.id] === ''"
style="margin: 5px 0"
>
<span style="padding: 5px 0">[goploy ~]#</span>
<div v-loading="traceDetail[item.id] === ''" style="margin: 5px 0">
<span>[goploy ~]#</span>
<el-button
v-if="item.state === 1 && !(item.id in traceDetail)"
type="text"
@ -255,7 +266,7 @@
<span v-else style="white-space: pre-line; padding: 5px 0">
{{ traceDetail[item.id] }}
</span>
</el-row>
</div>
</div>
</div>
<el-tabs v-model="activeRomoteTracePane" style="width: 100%">
@ -295,7 +306,9 @@
</template>
<template v-else-if="trace.type === 5">
<el-row style="margin: 5px 0" class="project-title">
<span style="margin-right: 5px">Rsync</span>
<span style="margin-right: 5px">
{{ projectRow.transferType.toUpperCase() }}
</span>
<span v-if="trace.state === 1" class="icon-success"></span>
<span v-else class="icon-fail"></span>
</el-row>
@ -353,7 +366,7 @@
</template>
<script lang="ts" setup>
import { Search, Refresh, Close } from '@element-plus/icons-vue'
import { Search, Refresh, Check, Close, Loading } from '@element-plus/icons-vue'
import RepoURL from '@/components/RepoURL/index.vue'
import {
DeployPreviewList,
@ -506,11 +519,8 @@ const getPreviewList = (projectId: number) => {
.then((response) => {
gitTraceList.value = response.data.list.map((item) => {
let element = <PublishTraceData & PublishTraceExt>item
if (element.ext !== '') {
Object.assign(element, JSON.parse(element.ext))
element.commit = element['commit']
? element['commit'].substring(0, 6)
: ''
if (element.ext) {
element.commit = element.ext.replaceAll('"', '').substring(0, 6)
}
return element
})
@ -540,8 +550,9 @@ const handlePageChange = (page: number) => {
const traceLoading = ref(false)
const activeRomoteTracePane = ref('')
const getPublishTrace = (publishToken: string) => {
traceLoading.value = true
let timeout: ReturnType<typeof setInterval>
function getPublishTrace(publishToken: string) {
traceLoading.value = true && !timeout
new DeployTrace({ lastPublishToken: publishToken })
.request()
.then((response) => {
@ -567,12 +578,28 @@ const getPublishTrace = (publishToken: string) => {
publishRemoteTraceList.value[trace.serverName].push(trace)
}
activeRomoteTracePane.value = Object.keys(publishRemoteTraceList.value)[0]
if (props.projectRow.lastPublishToken === publishToken) {
if (props.projectRow.deployState === 1 && !timeout) {
timeout = setInterval(() => {
getPublishTrace(publishToken)
}, 1000)
} else if (props.projectRow.deployState !== 1) {
clearInterval(timeout)
}
} else {
clearInterval(timeout)
}
})
.finally(() => {
traceLoading.value = false
})
}
const onClose = () => {
filterInpurtVisible.value = false
clearInterval(timeout)
}
const handleTraceChange: InstanceType<typeof ElRadioGroup>['onChange'] = (
lastPublishToken
) => {

View File

@ -178,7 +178,7 @@
<template v-else-if="trace.type === 5">
<el-row style="margin: 5px 0">
<div class="project-title">
<span style="margin-right: 5px">Rsync</span>
<span style="margin-right: 5px">Sync</span>
<span v-if="trace.state === 1" class="icon-success"></span>
<span v-else class="icon-fail"></span>
</div>

View File

@ -256,19 +256,39 @@
</el-button>
</el-row>
</el-form-item>
<el-form-item prop="rsyncOption">
<template #label>
<el-link
type="primary"
:href="$t('projectPage.rsyncDoc')"
target="_blank"
>
Rsync<br />
[OPTION...]
</el-link>
</template>
<el-form-item
:label="$t('projectPage.transferType')"
prop="transferType"
>
<el-radio-group v-model="formData.transferType">
<el-radio :label="'rsync'">
rsync
<el-link
:underline="false"
:href="$t('projectPage.rsyncDoc')"
target="_blank"
:icon="QuestionFilled"
style="color: #666"
/>
</el-radio>
<el-radio :label="'sftp'">
sftp
<el-link
:underline="false"
:href="$t('projectPage.sftpDoc')"
target="_blank"
:icon="QuestionFilled"
style="color: #666"
/>
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="$t('projectPage.transferOption')"
prop="transferOption"
>
<el-input
v-model="formData.rsyncOption"
v-model="formData.transferOption"
type="textarea"
:rows="3"
autocomplete="off"
@ -809,7 +829,8 @@ const tempFormData = {
afterDeployScript: '',
environment: 1,
branch: '',
rsyncOption: '-rtv --exclude .git',
transferType: 'rsync',
transferOption: '-rtv --exclude .git',
serverIds: [] as number[],
userIds: [] as number[],
review: 0,